Java With Eclipse

485

Transcript of Java With Eclipse

Page 1: Java With Eclipse
Page 2: Java With Eclipse

DAFTAR ISI

Java untuk Pemula

Bab I - Pendahuluan

Mengenal Bahasa Pemrograman Java

Kelebihan Java

Bab II - Instalasi

Eclipse IDE

Instalasi Subversive, Plugin SVN untuk Eclipse

Bab III - Memulai Java

Melihat lebih dekat “SelamatDatang”

Variabel dan Tipe Data

String, Objek dan Subrutin

Operator

Intermezzo : Membaca Input dari User

Intermezzo : Mengubah String ke int dan double

Bab IV - Memulai Java Lebih Lanjut - Struktur Kontrol

Blok, Perulangan, dan Percabangan

Perancangan Algoritma

Membuat kode, menguji, dan mencari kesalahan (debugging)

while dan do ... while

Perulangan for

Pernyataan if

Pernyataan switch

Bab V - Subrutin

Kotak Hitam

Subrutin Statik dan Variabel Statik

Parameter

Tipe Keluaran

Toolbox, API, dan Paket

Tentang Deklarasi

Bab VII - Kebenaran dan Ketangguhan Program

Pengenalan tentang Program yang Benar dan Tangguh

Membuat Program yang Benar

Page 3: Java With Eclipse

Pengecualian dan Pernyataan "try ... catch"

Pemrograman dengan Pengecualian

Bab VIII - Pengenalan Struktur Data dan Algoritma

Array

Pencarian, Pengurutan dan Rekursi

Struktur Data Berantai

Bab IX - Pemrograman Generik dan Kelas Koleksi

Pemrograman Generik

Pemrograman Generik pada Java

List

Set (Himpunan)

Kelas Map

Tabel Hash

Bab X - Pengenalan Input/Output (I/O)

Stream, Reader, dan Writer

File

Jaringan (network)

Pemrograman Serentak (Concurrency)

Pengenalan GUI dengan Eclipse SWT

Memulai SWT

o Widget-widget SWT

o Shell

o Kotak Dialog

o Dasar-dasar Kontrol dan Label

o Button (Tombol)

o Text

Page 4: Java With Eclipse

BAB I

PENDAHULUAN

A. Sejarah Java

Java dipelopori oleh James Gosling, Patrick Naughton, Chris Warth, Ed Frank,

dan Mike Sheridan dari Sun Microsystems, Inc pada tahun 1991. Mereka membutuhkan

kurang lebih 18 bulan untuk membuat versi pertamanya. Bahasa ini pada awalnya disebut

“Oak” tapi kemudian diubah menjadi “Java” pada tahun 1995 karena nama Oak telah

dijadikan hak cipta dan digunakan sebagai bahasa pemrograman lainnya. Antara

pembuatan Oak pada musim gugur 1992 hingga diumumkan ke publik pada musim semi

1995, banyak orang yang terlibat dalam desain dan evolusi bahasa ini. Bill Joy, Arthur van

Hoff, Jonathan Payne, Frank Yellin, dan Tim Lindholm merupakan kontributor kunci yang

mematangkan prototipe aslinya.

B. Mengenal Java

Java Modern

Java telah digunakan dalam banyak hal dan telah membuktikan keberadaannya

pada abad ke 21. Saat ini, Java digunakan bermacam jenis aplikasi seperti aplikasi

embedded, aplikasi keuangan, desktop, simulasi pesawat, pemrosesan citra, game, aplikasi

perusahaan terdistribusi yang disebut J2EE dan masih banyak lagi.

Java Virtual Machine (JVM)

Java Virtual Machine merupakan aplikasi sederhana yang ditulis dalam bahasa C

untuk mengeksi program yang ditulis dalam bahasa Java. Pada saat kompilasi (perubahan

dari bahasa tingkat tinggi ke bahasa lebih rendah), program tersebut diubah menjadi

KODE BYTE. Kemudian pada saat eksekusi, JVM membaca kode byte tersebu dan

mengubahnya menjadi bahasa mesin yang dimengerti oleh sistem operasi tempat program

tersebut dijalankan.

Karena JVM sangat bergantung pada platformnya (bahasa mesin merupakan

bahasa level rendah yang hanya dimengerti oleh suatu mesin tertentu, misalnya Intel, tapi

tidak dapat dimengerti oleh mesin lain, seperti Macintosh), byte code ini dapat dibuat

untuk terbebas dari kungkungan platform tertentu. Code byte yang dihasilkan dalam proses

kompilasi bahasa Java akan selalu sama untuk setiap sistem operasi atau jenis mesinnya,

tetapi JVM akan mengubah kode byte tersebut menjadi bahasa mesin tujuannya.

Just In Time Compiler (JIT)

Meskipun Java didesain untuk diinterpretasi, secara teknis tidak ada yang

menghalangi Java untuk dikompilasi menjadi bahasa mesin seperti bahasa-bahasa

Page 5: Java With Eclipse

pemrograman lainnya. Sun menyediakan kompiler Just In Time Compiler (JIT) untuk

mengkompilasi kode byte itu menjadi bahasa mesinnya pada saat yang bersamaan dengan

eksekusinya. Walaupun demikian, pendekatan JIT ini menghasilkan kemampuan yang

lebih dibandingkan dengan interpretasi biasa.

C. Kelebihan Java

Bahasa pemrograman lain yang telah ada sebelum Java lahir sudah merupakan

bahasa yang baik dan mudah dipelajasi oleh programmer profesional. Akan tetapi para

programmer ini menginginkan sesuatu yang baru yang memiliki banyak hal yang

menyelesaikan masalah mereka. Utamanya adalah keamanan kode mereka. Hal ini

melahirkan pikiran yang revolusioner untuk menemukan bahasa pemrograman lain yang

disebut Java. Tidak hanya keamanan tapi juga beberapa hal yang sering disebut sebagai

Java-Buzzwords. Kata-kata ini menjelaskan berbagai fitur tambahan dan beberapa hal yang

membuat Java demikian sukses dan diterima oleh dunia perangkat lunak. Berikut ini

adalah penjelasan serta keuntungan dari kata-kata tersebut.

Sederhana dan Berorientasi Objek

Seperti diuraikan sebelumnya, Java lahir dari suatu pemikiran mendalam akan

bahasa pemrograman yang ada pada saat itu, seperti C dan C++. Hal ini akan memudahkan

programmer profesional untuk dapat mengerti lebih jelas tentang Java, fungsionalitas, dan

lain sebagainya apabila ia memiliki pengetahuan dasar tentang C++ dan konsep

pemrograman berorientasi objek. Tujuannya agar konsep dasar dari teknologi Java dapat

dimengerti dengan mudah, dan programmer dapat segera menghasilkan sesuatu sedini

mungkin. Tidak hanya ini, penemu Java memastikan bahwa Java juga bermula dari bahasa

pemrograman dasar yang sudah ada pada saat itu. Kemudian mereka membuang berbagai

fitur yang rumit dan membingungkan.

Bahasa pemrograman Java didesain sejak awal untuk menjadi bahasa yang

berorientasi objek. Setelah kira-kira 30 tahun, akhirnya teknologi objek menjadi kenyataan

dan diterima oleh sebagian besar komunitas pemrograman. Konsep berorientasi objek

memungkinkan pembuatan software yang kompleks, berbasis network, sehingga dapat

disimpulkan bahwa teknologi Java menghasilkan platform pembuatan perangkat lunak

yang baik dan efisien serta berorientasi objek.

Keuntungan yang Anda dapat dari Java

Mulai dengan cepat: Java merupakan bahasa pemrograman berorientasi objek,

mudah dipelajari, terutama untuk programmer yang sudah menguasai C atau C++

Page 6: Java With Eclipse

Tulis lebih sedikit program: Jumlah kelas, jumlah metode, dll, menunjukkan

bahwa program yang ditulis dalam bahasa pemrograman Java memiliki jumlah 4

kali lipat lebih kecil dari program sama yang ditulis dalam bahasa C++

Tulis program lebih baik: Bahasa pemrograman Java menganjurkan praktek

membuat program yang baik, dan automatic garbage collection membantu Anda

untuk menghindari kebocoran memori. Orientasi objeknya, arsitektur komponen

JavaBeans, dan jangkauannya yanga luas, API yang mudah diperluas,

memungkinkan Anda menggunakan kode yang ada.

Membuat program dengan lebih cepat: Bahasa pemrograman Java lebih mudah

dari C++, pemrograman akan menjadi 2 kali lipat lebih cepat, dengan jumlah baris

yang jauh lebih sedikit.

Menghindari kebergantungan pada platform tertentu: Anda dapat menjalankan

program Anda pada banyak platform dengan TIDAK menggunakan library yang

ditulis spesifik untuk platform tertentu.

Tulis sekali, jalankan di mana saja: Karena aplikasi yang ditulis dalam bahasa

Java dikompilasi ke dalam kode byte yang bebas platform, aplikasi yang ditulis

dapat jalan secara konsisten pada platform apa saja.

Distribusikan software Anda dengan mudah: Dengan Java Web Start, pengguna

program Anda akan dapat menggunakan aplikasi Anda dengan mudah. Sistem

pengecekan versi otomatis pada saat program dimulai menjamin pengguna Anda

selalu menjalankan versi terkini. Apabila versi baru tersedia, Java Web Start akan

melakukan instalasi secara otomatis.

Software yang digunakan :

Eclipse 3.4 Ganymede digunakan dalam situs ini sebagai IDE (integrated development

environment)

atau Java 6 SDK

Bab II - Instalasi

Eclipse merupakan komunitas open source yang bertujuan menghasilkan platform

pemrograman terbuka. Eclipse terdiri dari framework yang dapat dikembangkan lebih

lanjut, peralatan bantu untuk membuat dan memanage software sejak awal hingga

diluncurkan. Platform Eclipse didukung oleh ekosistem besar yang terdiri dari vendor

tekonologi, start-up inovatif, universitas, riset institusi serta individu.

Page 7: Java With Eclipse

Banyak orang mengenal Eclipse sebagai IDE (integrated development environment) untuk

bahasa Java, tapi Eclipse lebih dari sekedar IDE untuk Java.

Komunitas Eclipse memiliki lebih dari 60 proyek open source. Proyek-proyek ini secara

konsep terbagi menjadi 7 categori :

1. Enterprise Development

2. Embedded and Device Development

3. Rich Client Platform

4. Rich Internet Applications

5. Application Frameworks

6. Application Lifecycle Management (ALM)

7. Service Oriented Architecture (SOA)

Secara umum Eclipse digunakan untuk membangun software inovatif berstandar industri,

dan alat bantu beserta frameworknya membantu pekerjaan menjadi lebih mudah.

Lisensi

Eclipse menggunakan EPL (Eclipse Public License), yaitu lisensi yang memungkinkan

organisasi untuk menjadikan Eclipse sebagai produk komersialnya, dan pada saat yang

sama meminta orang yang melakukan perubahan untuk mengkontribusikan hasilnya

kembali kepada komunitas.

Instalasi

Anda membutuhkan Java 5 JRE untuk menjalankan Eclipse.

Download Eclipse IDE for Java Developers untuk menggunakan kode pada situs

Belajar Java ini.

Gunakan utility pada sistem operasi anda untuk membuka kompresi file tersebut ke

dalam hard disk anda.

Catatan untuk Windows: Apabila Anda menggunakan utilitas kompresi file yang

berasal dari Windows XP atau Windows Vista itu sendiri, kadang kala utilitas

tersebut tidak berhasil membuka file dengan nama yang panjang. Jika Anda

mengalami masalah dekompresi Eclipse pada Windows, letakkan hasil dekompresi

pada root directory (misalnya C:\eclipse) atau gunakan software dekompresi lain

yang gratis seperti 7-Zip

Menjalankan Eclipse untuk pertama kali

Cari file bernama eclipse.exe (pada Windows) atau eclipse (pada Ubuntu)

kemudian double-click

Page 8: Java With Eclipse

Pada saat Eclipse pertama kali dijalankan, Eclipse akan menanyakan workspace,

yaitu folder tempat proyek dan data diletakkan. Anda bisa menempatkan di mana

saja asalkan jangan di dalam folder Eclipse itu sendiri.

Click Browse dan pilih folder yang ada inginkan. Tik "Use this as default and do

not ask again"

Halaman pembuka akan muncul. Klik "Workspace", tombol paling kanan

berbentuk anak panah untuk masuk ke dalam workspace Anda.

Program Java pertama Anda

Klik "File -> New -> Java Project"

Isi nama proyek (misalnya SelamatDatang), kemudian klik "Finish"

Setelah Eclipse membuat proyek untuk Anda, di bagian kiri workspace Anda akan

melihat struktur direktori proyek Anda yang dimulai dengan nama proyek, folder

src, dan folder JRE System Library

Klik kanan pada folder src, kemudian "New -> Package"

Isi nama package (misalnya selamatdatang), kemudian klik "Finish"

Klik kanan lagi pada folder selamatdatang, kemudian "New -> Class"

Isi nama class (misalnya SelamatDatang)

Karena

class ini adalah class utama yang akan langsung dijalankan oleh JRE (Java Runtime

Page 9: Java With Eclipse

Environment), click "public static void main(String[] args)" pada bagian "Which

method stubs would you like to create?"

Klik "Finish"

Eclipse akan membuat program kosong yang berisi package dan class sesuai

dengan nama yang Anda masukkan pada tahap sebelumnya

Sekarang ketik program berikut di bawah "// TODO"

System.out.println("Selamat Datang!");

Kemudian simpan hasilnya

Menjalankan program Java pertama Anda

Untuk menjalankan program Anda, klik "Run -> Run"

Di bagian bawah pada tab yang berjudul "Console" hasil program Anda

ditampilkan

Program ini akan menampilkan tulisan Selamat Datang! seperti pada gambar

berikut ini

Page 10: Java With Eclipse

Dalam situs ini, akan ada beberapa program yang cukup panjang dan mungkin

menyusahkan Anda apabila harus mengetik satu per satu. Anda dapat juga mendownload

berkas terkompresi untuk kemudian diimport ke dalam Eclipse.

Download file ini ke komputer Anda

Pada Eclipse, klik "New -> Import -> General -> Existing Project into

Workspace"

Lalu tik "Select Archieve", pilih file yang telah Anda download tersebut, dan klik

"Finish"

Instalasi Subversive, Plugin SVN untuk Eclipse

Untuk menggunakan contoh kode pada situs ini, Anda juga bisa menggunakan Subversion

untuk mengambil file langsung dari gudang SVN. Untuk menginstall Plugin Subversive,

dengan melakukan langkah-langkah berikut ini :

Page 11: Java With Eclipse

1. Klik "Help -> Software Updates -> Available Software"

2. Klik

tombol "Add Site", kemudian masukkan alamatnya sebagai berikut :

http://subclipse.tigris.org/update_1.4.x

3. Setelah mengambil data plugin yang tersedia, pilih beberapa plugin seperti pada gambar

berikut, lalu klik "Install" :

4. Window baru akan muncul, klik "Finish", jangan lupa untuk mengaccept license

agreementnya.

Page 12: Java With Eclipse

Setelah Anda berhasil melakukan instalasi plug-in Subversive, langkah-langkah berikut ini

akan membimbing Anda untuk menguji koneksi ke gudang (repository) kode pada SVN

server.

1. Klik "New -> Project -> SVN -> Checkout Project from SVN"

2. Klik "Create a new repository location"

3. Masukkan http://belajarjava.googlecode.com/svn/trunk

4. Pilih folder selamatdatang dan klik Finish

5. Project baru akan tersedia di sebelah kiri workspace Anda. Klik "Run -> Run" dan pilh

"Java Application" untuk menjalankan program ini.

6. Selamat, Anda telah mempersiapkan software yang diperlukan.

Bab III - Memulai Java

Program komputer adalah rangkaian instruksi yang diberikan agar komputer dapat bekerja.

Suatu pekerjaan yang mungkin sederhana bagi manusia tidak dapat dimengerti oleh

komputer. Manusia harus memberikan petunjuk kepada komputer bagaimana melakukan

suatutugas dalam bentuk bahasa pemrograman. Bahasa pemrograman berbeda dengan

bahasa manusia, karena komputer membutuhkan aturan yang lebih baku apa yang boleh

Page 13: Java With Eclipse

dan apa yang tidak boleh dalam suatu bahasa pemrograman. Aturan ini disebut sintaks

bahasa.

Sintaks bahasa pemrograman ditentukan berdasarkan apa yang bisa dilakukan oleh

komputer, misalnya loop (perulangan), cabang (branch), atau fungsi. Hanya program

dengan sintaks yang benar yang dapat dikompilasi atau diinterpretasi yang pada akhirnya

bisa dijalankan di komputer. Kompiler akan memberikan pesan kesalahan apabila ada

kesalahan dalam sintaks sehingga kita memperbaikinya.

Untuk menjadi programmer yang sukses, kita harus mengerti secara detail sintaks dari

bahasa pemrograman yang kita akan gunakan. Tetapi, sintaks hanya sebagian cerita. Kita

ingin program yang kita buat berjalan sesuai dengan yang kita inginkan. Artinya program

tersebut harus benar secara logika. Program yang benar secara logika disebut memiliki

semantik yang benar.

Di situs ini kita akan mempelajari tentang sintaks dan semantik dari dari setiap bahasa

pemrograman Java. Sintaks mudah dihafal, tetapi semantik lebih seperti perasaan. Untuk

itu, coba download dan jalankan contoh-contoh program sehingga kita dapat memahami

bagaimana setiap program bekerja.

Melihat lebih dekat “SelamatDatang”

Applikasi SelamatDatang memiliki 4 komponen, yaitu :

definisi paket (package)

komentar

definisi kelas (class)

metode main

package selamatdatang;

public class SelamatDatang {

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

System.out.println("Selamat Datang!");

}

}

Page 14: Java With Eclipse

Mari kita bahas satu per satu.

Definisi paket (package)

package selamatdatang;

Package dalam Java merupakan kumpulan dari berbagai kode yang terangkum dalam satu

paket. Untuk memudahkan penulisan dan pembagian logika suatu program, satu paket

terbagi menjadi beberapa berkas (file) di mana setiap file memiliki fungsi atau tugas yang

sangat khusus, misalnya satu file berfungsi untuk mendeklarasikan konstanta dan kelas,

sementara file yang lain berisi implementasi kelas dan prosedurnya.

Pada contoh aplikasi SelamatDatang di atas, paket ini hanya berisi satu buah file yang

isinya terdiri dari satu kelas dan satu metode.

Definisi paket tidak selalu diperlukan, tetapi hal ini merupakan kebiasaan baik untuk

melatih kita berfikir secara logis dan sistematis.

Komentar

/**

* @param args

*/

// TODO Auto-generated method stub

Komentar tidak akan diproses oleh kompiler tetapi berguna bagi programmer lain. Bahasa

Java memiliki 3 jenis komentar :

/* text */ — Compiler akan mengabaikan kata kata antara /* dan */

/** documentation */ — Ini merupakan komentar yang dipergunakan khusus untuk

dokumentasi. Kompiler akan mengabaikan komentar dari /* hingga */. Alat bantu

javadoc akan memproses komentar dokumentasi untuk membuat dokumentasi

secara otomatis dari sumber program.

// text — Kompiler akan mengabaikan segala sesuatu dari // hingga akhir baris

Definisi Kelas

public class SelamatDatang {

...

}

Kelas merupakan bagian integral dari bahasa Java karena Java merupakan bahasa

berorientasi objek. Setiap aplikasi harus terdiri dari satu kelas. Di sini kita definisikan

kelas SelamatDatang sebagai kelas utama.

Metode main

Dalam bahasa pemrograman Java, setiap aplikasi harus memiliki satu buah metode main

yang bentuknya seperti berikut :

Page 15: Java With Eclipse

public static void main(String[] args) {

...

}

Metode main mirip dengan fungsi main pada bahasa C/C++ di mana fungsi ini merupakan

pintu gerbang dimulanya suatu program. Metoda main dapat dipanggil dengan

menyertakan variabel, baik hanya satu variabel, banyak variabel atau bahkan tidak ada

sama sekali.

Yang terakhir adalah perintah berikut untuk menampilkan Selamat Datang pada komputer

Anda.

System.out.println("Selamat Datang!");

Perintah tersebut menggunakan pustaka inti Java, yaitu kelas Sistem.

Variabel dan Tipe Data

Nama merupakan hal penting dalam teknik pemrograman. Dalam suatu program, nama

digunakan untuk menyebut sesuatu. Untuk menggunakan "sesuatu" tersebut, programmer

harus mengerti bagaimana aturan pemberian nama dan aturan untuk menggunakan nama

tersebut dalam programnya. Atau dengan kata lain, programmer harus mengerti sintaks dan

semantik dari nama.

Menurut aturan sintaks Java, nama merupakan rangkaian dari 1 atau lebih karakter. Harus

dimulai dengan huruf dan harus terdiri dari huruf, angka atau karakter garis bawah '_'.

Berikut ini adalah contoh nama yang diperbolehkan:

N n rate x15 quite_a_long_name HelloWorld

Hurus kecil dan huruf besar dianggap berbeda, sehingga HelloWorld, helloworld,

HELLOWORLD, dan heLLOwoRLD adalah nama yang berbeda-beda. Beberapa kata tertentu

merupakan nama yang memiliki kegunaan tertentu oleh Java, dan tidak dapat dipergunakan

oleh programmer. Kata-kata ini disebut reserved words (kata-kata cadangan) misalnya:

class, public, static, if, else, while, dan lain-lain.

Hal lain yang penting adalah compound names atau nama campuran, yaitu yang

merupakan nama biasa yang dihubungkan dengan titik, misalnya System.out.println.

Idenya adalah "sesuatu" pada Java dapat terdiri dari "sesuatu" yang lain. Nama

System.out.println artinya System menampung out dan out menampung println. Kita

sebut "sesuatu" sebagai identifier (pengenal) tidak peduli apakah ia berupa nama tunggal

atau campuran.

Page 16: Java With Eclipse

Program melakukan manipulasi data yang disimpan dalam memori. Dalam bahasa mesin,

data hanya bisa diambil dengan menyebut alamatnya di memori. Dalam bahasa

pemrograman tingkat tinggi seperti Java, nama bisa digunakan untk mengganti alamat data

tersebut di memori. Tugas komputer adalah untuk melacak di mana data tersebut di

simpan, sedangkan programmer menggunakan nama untuk memerintahkan komputer

mengambil data tersebut dari memori. Nama yang digunakan seperti ini disebut variable.

Variabel sebenarnya berarti lebih kompleks. Variabel bukan isi dari data, tapi lokasi di

memori yang menyimpan data tersebut. Variabel bisa diibaratkan sebagai kotak

penyimpanan data, bukan isi kotaknya. Karena data di dalam kotak bisa berubah, variabel

hanya dapat dipercaya pada satu saat saja, walaupun tempatnya selalu sama.

Dalam bahasa Java, satu-satunya cara untuk memasukkan data ke dalam variabel adalah

dengan menggunakan assignment statement, atau pernyataan pemberian nilai. Pernyataan

ini berbentuk:

variable = ekspresi;

di mana ekspresi menyatakan apapun yang berhubungan dengan nilai suatu data. Ketika

komputer menjalankan instruksi ini, komputer akan menghitung dan menyimpan hasilnya

ke dalam variabel. Contoh:

kecepatan = 40;

Variable dalam pernyataan di atas adalah kecepatan dan ekspresinya adalah angka 40.

Komputer menghitung pernyataan ini dan menyimpan 40 ke dalam variabel kecepatan,

dan mengganti apapun yang telah disimpan sebelumnya.

Sekarang misalnya kita ingin melakukan perhitungan yang lebih kompleks:

jarak = kecepatan * waktu

Di sini, * merupakan operasi perkalian. Komputer mengambil data yang disimpan dalam

variabel kecepatan dan waktu, melakukan perkalian, dan menyimpan hasilnya dalam

jarak.

Variabel dalam bahasa Java didesign untuk menyimpan hanya 1 jenis tipe data. Kompiler

akan menampilkan kesalahan sintax apabila variabel ini dicoba untuk diberi tipe data jenis

lain. Oleh karena itu Java disebut bahasa pemrograman bertipe kuat atau strongly typed

language.

Ada 8 tipe data primitif dalam bahasa Java.

Jenis

DataDeskripsi Ukuran Minimum Maksimum

Page 17: Java With Eclipse

boolean

Hanya bisa

berisi benar

atau salah

1-bit

charKarakter

Unicode16-bit

byte Bilangan bulat 8-bit -127 128

short Bilangan bulat 16-bit -32768 32767

int Bilangan bulat 32-bit -2147483648 2147483647

long Bilangan bulat 64-bit -9223372036854775808 9223372036854775807

float Bilangan riil 32-bit1.40129846432481707e-

453.40282346638528860e+38

double Bilangan riil 64-bit4.94065645841246544e-

3241.79769313486231570e+308

Suatu variabel baru dapat digunakan apabila telah dideklarasikan. Pernyataan deklarasi

variabel digunakan untuk mendeklarasikan satu atau lebih variabel dan memberinya nama.

Ketika komputer mengeksekusi deklarasi variabel, komputer akan menyediakan ruangan di

memori kemudian menyimpan alamat ini sesuai dengan nama variabel yang diberikan.

Deklarasi variable berbentuk seperti :

nama_tipe nama_variabel;

nama_variabel dapat berupa sebuah nama variabel atau beberapa nama sekaligus yang

dipisah dengan koma. Gaya pemrograman yang baik yaitu dengan mendeklarasikan satu

variabel dalam satu pernyataan, kecuali variabel tersebut berhubungan erat satu sama lain.

Misalnya:

float num;

String nama;

String nama;

boolean bol;

int x,y;

Atau pendeklarasian variabel bisa juga dilakukan sekaligus dengan pemberian nilainya,

seperti pada contoh berikut:

int num = 1000;

char ch = 'e';

float angka = -1.504;

boolean bol = true;

Page 18: Java With Eclipse

Jenis-jenis Variabel

Java memiliki beberapa jenis variabel yang dapat dikelompokkan sebagai berikut :

Instance Variables (tidak statis). Dalam bahasa pemrograman berorientasi objek,

objek menyimpan variabel yang tidak dideklarasikan dengan kata kunci static

dalam kategori non-statis, atau dapat berubah-ubah. Suatu kelas dapat dijelmakan

ke dalam beberapa objek. Nilai yang terkandung dalam variabel tak-statis ini

berbeda untuk setiap objeknya.

Class Variables (statis). Variabel ini merupakan bagian integral dari suatu kelas,

dan tidak ada satu objek pun yang dapat menyatakan kepemilikan atas variabel ini.

Variabel yang dideklarasikan sebagai statis digunakan bersama oleh semua objek.

Variabel ini lebih bersifat global yang nilainya sama untuk setiap objek pada kelas

yang bersangkutan.

Local Variables. Variabel ini didefinisikan di dalam suatu metoda (method) atau

dalam suatu prosedur. Variabel ini bersifat lokal karena hanya dapat diakses oleh

metoda atau prosedur tersebut.

Parameter. Paramater atau argumen adalah variabel yang digunakan pada saat suatu

metoda atau prosedur dipanggil. Parameter berguna untuk memberikan nilai awal

untuk diteruskan (pass) ke dalam suatu prosedur atau metoda.

Literal

Pada bagian ini akan dijelaskan tentang literal, yaitu rangkaian kata atau huruf yang

menyatakan suatu nilai. Misalnya

int angka = 10;

Pada pernyataan di atas, yang dinamakan literal adalah 10, karena 10 berarti bilangan bulat

atau integer. Pada bahasa pemrograman java, terdapat beberapa jenis literal yang

melambangkan bilangan bulat, riil, kalimat, atau boolean.

Literal Bilangan Bulat

Bilangan bulat dapat dilambangkan dalam beberapa bentuk. Bilangan bulat biasa

dilambangkan dengan deretan angka yang dimulai dengan angka yang bukan nol.

int angka = -10;

Page 19: Java With Eclipse

Bilangan oktal adalah bilangan bulat berbasis 8, yang berarti hanya dapat terdiri dari

angka-angka 0 hingga 7. Bilangan oktal ditulis seperti bilangan bulat biasa dimulai dengan

0.

// 22 basis delapan atau 18 dalam desimal

int angka = 022;

Bilangan heksadesimal adalah bilangan berbasis 16. Bilangan heksadesimal dilambangkan

dengan 0 hingga 9 dan a hingga f dan dimulai dengan 0x.

// 2a heksadesimal atau 42 dalam desimal

int angka = 0x2a;

Long integer. Seperti dijelaskan pada bab terdahulu long integer membutuhkan memori

sebesar 64bit, yang artinya kita dapat menyimpan bilangan bulat hingga 2 ^ 64. Untuk

merepresentasikan long integer, tambahkan huruf L pada akhir bilangan.

int angka = 22L;

Literal Karakter

Kita dapat melambangkan suatu karakter dengan tanda petik tunggal misalnya ‘a’ atau ‘3′

atau ‘=’. Suatu karakter dapat juga dilambangkan dengan kode ASCII nya. Caranya dengan

memulainya dengan \u00 (garis miring terbalik) kemudian diikuti dengan kode ASCII nya

dalam bentuk heksadesimal.

// huruf 'A' dalam ASCII

char huruf = '\u0041';

Selain itu ada beberapa karakter lain selain alfabet yang dapat dilambangkan dengan

escape sequence. Berikut ini beberapa contohnya.

\n = tombol enter atau baris baru.

\r = carriage return.

\t = tombol tab.

\b = tombol backspace.

\\ = karakter \

\’ = karakter ‘

\” = karakter ”

Literal Boolean

Nilai true dan false pada java merupakan literal boolean. Suatu variabel bertipe boolean

hanya dapat memiliki nilai true atau false.

boolean ok = true;

Ingat bahwa boolean true atau false TIDAK menggunakan tanda petik tunggal seperti

ekspresi pada karakter.

Page 20: Java With Eclipse

Literal Bilangan Riil

Bilangan riil, misalnya -0.00127 atau 415.842, pada java dapat disimpan baik sebagai float

atau double. Bilangan real dapat direpresentasikan dalam bentuk desimal biasa, pecahan,

atau eksponen (dilambangkan dengan e atau E).

UkuranTipe

bytes bitRentang Presisi (jumlah digit)

float 4 32 +/- 3.4 x 1038 6-7

double 8 64 +/- 1.8 x 10308 15

Imbuhan akhir d atau D dan f atau F dapat pula ditambahkan untuk menentukan tipenya

secara eksplisit. Berikut beberapa contohnya.

double d = 3.27E+32;

float f = 4f;

float pi = 3.14159;

Literal String

String merupakan untaian huruf dan angka yang tersusun menjadi satu kalimat. Dalam

bahasa java, string bukan merupakan tipe primitif, tetapi merupakan kelas. String pada java

tidak disimpan dalam bentuk array seperti pada C. Java menyediakan beberapa metoda

untuk melakukan penggabungan, modifikasi, atau perbandingan. String ditulis di antara

dua tanda petik ganda seperti contoh berikut.

String salam = "Selamat Datang";

String juga dapat mengandung karakter spesial seperti dibahas pada literal karakter.

Misalnya

String hallo = "Selamat Datang \"Bapak Presiden\"";

System.out.println("Hallo Bambang\nSelamat pagi,\nSemoga hari anda

cerah\n";

Berikut ini adalah beberapa contoh lainnya.

// Contoh string kosong

String teks = "";

// Contoh string berisi "

teks = "\"";

// String dapat juga dipisah menjadi beberapa baris

teks = "ini baris pertama " +

"dan ini lanjutannya."

Literal Null

Page 21: Java With Eclipse

Literal terakhir pada bahasa java adalah literal null. Null merupakan kondisi di mana suatu

objek tidak diberi alokasi memori. Pada saat suatu objek dideklarasikan, komputer akan

mengalokasikan memori untuk objek tersebut. Apabila objek tersebut telah selesai

dipergunakan, kita dapat melepas lokasi memori yang digunakan oleh objek tersebut

sehingga memori itu dapat digunakan oleh objek lain. Berikut ini adalah contohnya

obj = null;

Literal null berfungsi memberi tahu garbage collector (pemulung memori) bahwa objek

tersebut tidak lagi terpakai. Kemudian memori akan dilepaskan agar dapat digunakan oleh

objek lain.

Contoh Program Variabel dan Tipe Data

Berikut ini adalah listing program penghitungan bunga bank. Kode program dapat anda

unduh dalam bentuk zip file atau melalui SVN di alamat berikut :

http://belajarjava.googlecode.com/svn/trunk/BungaBank

Lihat cara mengimpor contoh-contoh program ke dalam Eclipse di Bab II - Instalasi.

package bungabank;

public class BungaBank {

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

/* Deklarasi variable */

double pokok; // nilai investasi

double sukubunga; // suku bunga bank

double bunga; // nilai bunga

/* Perhitungan */

pokok = 20000;

sukubunga = 0.10; // sama dengan 10%

bunga = pokok * sukubunga;

pokok = pokok + bunga;

/* Cetak hasil keluaran */

System.out.print("Bunga yang dihasilkan adalah Rp. ");

Page 22: Java With Eclipse

System.out.println(bunga);

System.out.print("Nilai investasi setelah 1 tahun adalah

Rp. ");

System.out.println(pokok);

}

}

Berikut adalah hasil keluarannya :

String, Objek dan Subrutin

Bagian sebelumnya memperkenalkan 8 jenis tipe data primitif dan tipe data String.

Perbedaan mendasar antara tipe primitif dan String adalah : nilai dari tipe String berupa

objek. Objek akan dibahas pada bagian lain mengenai kelas (class). Di bagian ini kita akan

belajar bagaimana String digunakan dan juga mempelajari konsep pemrograman penting

yang lain, yaitu subrutin.

Subrutin adalah kumpulan instruksi program yang digabungkan sehingga memiliki fungsi

tertentu. Dalam bahasa Java, setiap subrutin termasuk di dalam bagian suatu kelas atau

objek. Java menyediakan kelas standar yang siap digunakan. Nilai tipe String adalah objek

Page 23: Java With Eclipse

yang memiliki kumpulan subrutin yang dapat digunakan untuk memanipulasi String

tersetbut. Kita bisa memanggil subrutin tersebut tanpa harus mengerti bagaimana subrutin

tersebut bekerja. Sebenarnya ini adalah tujuan subrutin, yaitu sekumpulan perintah yang

memiliki fungsi tertentu tanpa harus mengetahui secara detail apa yang terjadi di dalam.

Kelas terdiri dari variabel dan subrutin yang disimpan di dalamnya. Variabel dan subrutin

ini disebut static member (anggota statis). Contohnya adalah pada program

SelamatDatang, kelas SelamatDatang memiliki anggota main() yang merupakan

anggota statis kelas itu. Anggota yang berupa static member diberi imbuhan static,

seperti pada main().

Fungsi kelas yang lain adalah untuk mendeskripsikan suatu objek. Kelas di sini berfungsi

seperti tipe data. Nilai yang disimpan dalam tipe data itu adalah objek. Misalnya String

sebenarnya merupakan nama kelas yang disediakan bahasa Java. String juga berupa tipe

data dan kalimat seperti "Selamat Datang!" adalah isi dari tipe data String.

Kegunaan kelas baik sebagai gabungan subrutin ataupun sebagai objek sering

membingungkan. Misalnya kelas String juga menyimpan beberapa subrutin statik selain

juga sebagai tipe data. Contoh kelas standar lainnya adalah Math yang berisi kumpulan

subrutin statik untuk melakukan berbagai perhitungan matematis.

Kelas sebagai tipe data

Mari mulai dengan melihat kembali subrutin System.out.print. Subrutin ini digunakan

untuk menampilkan pesan kepada user. Misalnya System.out.print("Selamat

datang!") menampilkan pesan Selamat datang!

System merupakan salah satu kelas standar Java. Salah satu anggotanya bernama out.

Karena variable ini berada di dalam kelas System, kita harus memanggilnya dengan nama

lengkapnya yaitu System.out. Variabel System.out merupakan suatu objek, dan objek itu

memiliki subrutin yang bernama print. Pengenal campuran System.out.print mengacu

pada subrutin print di dalam objek out dalam kelas System.

(Untuk melihat dari sisi lain, sebetulnya System.out merupakan objek dari kelas

PrintStream. PrintStream adalah salah satu kelas standar Java. Objek dengan tipe

PrintStream adalah objek tempat informasi bisa dicetak. Setiap objek yang bertipe

PrintStream memiliki subrutin bernama print yang bertugas untuk mencetak sesuatu ke

medium tersebut. Dalam hal ini System.out adalah tujuan tempat print dilakukan. Objek

Page 24: Java With Eclipse

lain yang bertipe PrintStream bisa mengirim informasi ke tujuan lain, misalnya file atau

network atau ke komputer lain.

Ini adalah esensi dari pemrograman berorientasi objek, di mana beberapa hal yang

memiliki kesamaan - dalam hal PrintStream sama-sama sebagai tempat tujuan print -

dapat digunakan dengan cara yang sama, yaitu memanggil subrutin print.)

Karena nama variabel, nama kelas, dan nama subrutin berbentuk hampir sama, ada baiknya

untuk membedakannya sehingga di kemudian hari program dapat dibaca dengan jelas.

Kelas standar Java menggunakan nama yang selalu dimulai dengan huruf besar, sedangkan

nama variabel dimulai dengan huruf kecil. Ini bukan sintaks Java, tetapi lebih seperti

konvensi. Setiap orang bebas menggunakan konvensinya masing-masing, tetapi apabila

pemrograman dilakukan secara bersama-sama, seperti pada open source programming,

konvensi yang sama akan sangat memudahkan orang lain membaca dan menguji program

yang kita buat. Nama subrutin juga dimulai dengan huruf kecil, sama dengan nama

variabel. Tetapi subrutin harus diikuti dengan tanda buka kurung sehingga masih mudah

dibedakan dengan nama variabel.

Kelas sebagai kumpulan subrutin dan variabel statik

Selain sebagai tipe data, kelas System juga memiliki subrutin statik yang bernama exit.

Memanggil System.exit akan menghentikan jalannya program, dan digunakan apabila

karena suatu hal (misalnya program telah selesai atau adanya error) program harus

dihentikan. Subrutin exit menerima parameter bilangan bulat, misalnya System.exit(0)

atau System.exit(1), untuk memberitahu kepada sistem operasi alasan kenapa program

dihentikan. Status 0 berarti program berhenti normal tanpa adanya error, sedangkan status

lain berarti ada sesuatu yang salah yang terjadi di tengah eksekusi program tersebut.

Setiap subrutin melakukan fungsi tertentu. Beberapa di antaranya melakukan perhitungan

lalu melaporkan hasil perhitungannya. Ada juga yang berfungsi untuk mengambil data dari

tempat tertentu dan melaporkan data yang didapat. Subrutin jenis ini disebut function

(fungsi). Fungsi adalah subrutin yang mengembalikan (return) suatu nilai. Nilai yang

dikembalikan akan digunakan lebih lanjut dalam program.

Sebagai contoh adalah fungsi untuk menghitung akar kuadrat dari suatu bilangan. Java

memiliki fungsi seperti itu yang disebut Math.sqrt. Fungsi ini merupakan anggota statik

dari kelas yang bernama Math. Jika x adalah suatu bilangan, makan Math.sqrt(x)

Page 25: Java With Eclipse

menghitung akar kuadrat dari x dan mengembalikan hasil perhitungannya. Karena

Math.sqrt(x) mengembalikan suatu bilangan, kita tidak bisa memanggil fungsi ini seperti

Math.sqrt(x); // Tidak masuk akal!

Kita tentunya ingin menggunakan hasilnya untuk instruksi selanjutnya, misalnya

menuliskan hasilnya di layar seperti

System.out.print( Math.sqrt(x) ); // Menampilkan akar kuadrat x di

layar.

atau menyimpan hasilnya pada variabel lain, seperti

panjangDiagonal = Math.sqrt(x);

Kelas Math

Kelas Math memiliki banyak fungsi statik. Beberapa yang penting di antaranya:

Math.abs(x), menghitung nilai mutlak (absolut) dari x. Nilai mutlak bilangan

negatif adalah bilangan positif, dan bilangan positif tetap bilangan positif.

Fungsi trigonometri Math.sin(x), Math.cos(x), and Math.tan(x). (Untuk semua

fungsi trigonometri, sudut memiliki satuan radian, bukan derajat)

Fungsi trigonometri inverse, yang mencari sudut dari suatu nilai trigonometric,

kebalikan dari fungsi trigonometri, seperti arcus sin, arcus cos, dan arcus tangen.

Math.asin(x), Math.acos(x), and Math.atan(x).

Math.exp(x), menghitung pangkat dari bilangan natural e, atau ex. Dan logaritma

natural loge x atau ln x bisa dihitung dengan menggunakan fungsi Math.log(x).

Math.pow(x,y) menghitung xy atau x pangkat y

Math.floor(x) menghitung pembulatan ke bawah dari suatu bilangan riil,

misalnya 3.84 akan dibulatkan ke bawah menjadi 3.0

Math.random() memilih bilangan acak di antara 0.0 dan 1.0. Komputer memiliki

algoritma perhitungan tertentu yang hasilnya bilangan acak (meskipun bulan

bilangan yang betul-betul acak, tetapi cukup untuk kebanyakan fungsi)

Paremeter (nilai di dalam kurung) fungsi-fungsi di atas bisa bertipe numerik apa saja

(misalnya double, int, dll), tetapi keluarannya bertipe double, kecuali abs(x) yang tipe

keluarannya sama dengan tipe parameternya.

Math.random() tidak memiliki parameter, tetapi tanda kurungnya harus tetap ditulis untuk

membedakan fungsi dan variabel. Contoh fungsi lain yang tidak memiliki parameter adalah

System.currentTimeMillis() yang berguna untuk mengambil waktu saat ini dalam

Page 26: Java With Eclipse

satuan milidetik, dihitung sejak 1 Januri 1970 waktu GMT. Satu milidetik sama dengan 1

per 1000 detik. Keluarannya bertipe long.

Untuk menghitung waktu yang diperlukan untuk menjalankan suatu perintah, jalankan

fungsi System.currentTimeMillis() sebelum dan sesudah suatu instruksi dijalankan.

Perbedaannya adalah waktu yang diperlukan untuk menjalankan suatu instruksi.

Contoh Kelas Math

Berikut ini adalah listing program untuk melakukan beberapa perhitungan matematika,

menampilkan hasilnya di layar, dan melaporkan waktu yang diperlukan untuk melakukan

perhitungan tersebut.

Kode program dapat anda unduh dalam bentuk zip file atau melalui SVN di alamat berikut

: http://belajarjava.googlecode.com/svn/trunk/WaktuKomputasi

Lihat cara mengimpor contoh-contoh program ke dalam Eclipse di Bab II - Instalasi.

package waktukomputasi;

public class WaktuKomputasi {

/**

* Program ini akan melakukan beberapa perhitungan matematika,

* menampilkan hasilnya di layar, dan melaporkan waktu yang

diperlukan

* untuk melakukan perhitungan tersebut

*

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

long waktuMulai;

long waktuSelesai;

waktuMulai = System.currentTimeMillis();

double panjang, lebar, sisiMiring; // sisi segitiga

siku-siku

panjang = 18.0;

lebar = 27;

sisiMiring = Math.sqrt(panjang*panjang + lebar*lebar);

Page 27: Java With Eclipse

System.out.print("Segitiga siku-siku dengan panjang 18

dan lebar 27 memiliki sisi miring ");

System.out.println(sisiMiring);

System.out.println("\nSecara matematis, sin(x)*sin(x) + "

+ "cos(x)*cos(x) - 1 = 0");

System.out.println("Mari kita cek untuk x = 1 :");

System.out.print("sin(1)*sin(1) + cos(1)*cos(1) - 1 = ");

System.out.println( Math.sin(1)*Math.sin(1)

+ Math.cos(1)*Math.cos(1) - 1 );

System.out.println("(Mungkin akan ada kesalahan desimal

dalam "

+ "perhitungan bilangan real!)");

System.out.print("\nIni adalah bilangan acak : ");

System.out.println(Math.random());

waktuSelesai = System.currentTimeMillis();

System.out.print("\nTotal waktu perhitungan : ");

System.out.print((waktuSelesai - waktuMulai)/1000.0);

System.out.println(" detik");

}

}

Berikut adalah hasil keluarannya :

Page 28: Java With Eclipse

Kelas String

Nilai suatu String adalah objek. Objek ini berisi rangkaian huruf yang membentuk string.

Objek tersebut juga berisi subrutin. Misalnya length adalah fungsi yang menghitung

panjang suatu string, atau jumlah karakter dalam suatu string. Misalnya string str yang

dideklarasikan sebagai berikut :

String str;

str = "Hari ini cerah sekali!"

Untuk menghitung jumlah karakter dalam string str, panggil fungsi str.length() yang

keluarannya bertipe int. Fungsi ini tidak membutuhkan parameter. Fungsi length

merupakan anggota kelas String dan dapat digunakan oleh semua data yang bertipe

String. Dan juga bisa digunakan oleh literal string, misalnya program berikut menghitung

jumlah karakter dalam string "Indonesia Raya" :

System.out.print("Jumlah karakter dalam \"Indonesia Raya\" adalah ");

System.out.print("Indonesia Raya".length());

System.out.println(" karakter");

Kelas String memiliki beberapa fungsi di antaranya :

Page 29: Java With Eclipse

s1.equals(s2) adalah fungsi yang mengembalikan nilai boolean (true atau

false). Fungsi ini akan menghasilkan true jika s2 sama dengan s1, dan salah jika

tidak. Kesamaan yang diuji adalah kesamaan persis baik kapitalnya maupun urutan

huruf-hurufnya.

s1.equalsIgnoreCase(s2) juga menghasilkan nilai boolean yang menguji apakah

string s2 sama dengan s1 dengan tidak membandingkan kapitalnya.

"Kucing".equalsIgnoreCase("kucing") menghasilkan true.

s1.length(). Seperti diulas sebelumnya, fungsi ini menghitung jumlah karakter

dalam string s1.

s1.charAt(N). N adalah integer (bilangan bulat). Fungsi ini mengembalikan

karakter ke-N dari string s1. Karakter pertama dihitung sebagai posisi ke-0.

s1.charAt(0) berarti mengambil karakter pertama, sedangkan s1.charAt(1)

berarti mengambil karakter ke-2, dan seterusnya. Karakter terakhir memiliki indeks

s1.length() - 1. Fungsi ini akan mengeluarkan pesan kesalahan apabila N

bernilai negatif atau lebih besar dari s1.length() - 1.

s1.substring(N,M), di mana N dan M bilangan bulat. Fungsi ini mengambil

potongan string antara karakter ke-N hingga karakter M-1. Catatan bahwa karakter

ke-M tidak ikut diambil. Misalnya, "jalan layang".substring(1,4)

menghasilkan "ala".

s1.indexOf(s2) mengembalikan nilai integer. Fungsi ini mencari string s2 di

dalam string s1, dan apabila ditemukan mengembalikan posisi awal s2 di dalam s1.

Jika tidak ditemukan, fungsi ini akan mengembalikan -1. Fungsi ini bisa juga

digunakan untuk mencari string s2 dalam s1 setelah posisi ke-N dalam string s1.

Misalnya, "kelapa muda".indexOf("a") menghasilkan 3, sedangkan "kelapa

muda".indexOf("a",6) menghasilkan 10.

s1.compareTo(s2) membandingkan s2 dan s1. Jika s1 dan s2 sama, hasilnya 0.

Jika s1 kurang dari s2, hasilnya bilangan negatif , dan jika s1 lebih besar dari s2,

hasilnya bilangan positif. "Kurang dari" atau "lebih dari" mengacu pada urutannya

dalam abjad jika keduanya huruf kecil atau keduanya huruf besar. Jika kapitalnya

berbeda, perbandingannya bergantung pada nilai ASCII-nya.

s1.toUpperCase() adalah fungsi untuk mengubah seluruh huruf dalam s1 menjadi

huruf besar.

Page 30: Java With Eclipse

s1.toLowerCase() berfungsi untuk mengubah huruf dalam s1 menjadi hurug

kecil.

s1.trim() adalah berfungsi menghapus karakter yang tak bisa dicetak, misalnya

spasi, baris baru, enter, yang ada sebelum atau sesudah suatu kalimat. Misalnya "

selamat pagi bu ".trim() menghasilkan "selamat pagi bu".

Untuk s1.toUpperCase(), s1.toLowerCase(), s1.trim() nilai s1 tidak berubah. Fungsi

ini melakukan perhitungan kemudian mengembalikan string baru hasil perhitungan

tersebut.

Kita bisa juga menggunakan tanda + menyambung 2 string. Misalnya "mata" + "hari"

menjadi "matahari". Nilai yang dapat disambungkan bukan hanya string dan string, tetapi

juga string dan angka, misalnya "jalan" + 2 menjadi "jalan2".

Contoh Kelas String

Berikut ini adalah listing program untuk melakukan beberapa contoh menggunakan kelas

String, menampilkan hasilnya di layar, dan melaporkan waktu yang diperlukan untuk

melakukan perhitungan tersebut.

Kode program dapat anda unduh dalam bentuk zip file atau melalui SVN di alamat berikut

: http://belajarjava.googlecode.com/svn/trunk/ContohString

Lihat cara mengimpor contoh-contoh program ke dalam Eclipse di Bab II - Instalasi.

package contohstring;

public class ContohString {

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

// demo String.length() dan operator + untuk

menghubungkan karakter

String str;

str = "Hari ini cerah sekali!";

System.out.println("Panjang string \"" + str + "\" adalah

" + str.length());

Page 31: Java With Eclipse

System.out.print("Jumlah karakter dalam \"Indonesia

Raya\" adalah ");

System.out.print("Indonesia Raya".length());

System.out.println(" karakter");

// demo equals dan compareTo

System.out.println("\"jambu\" = \"Jambu\"? " +

"jambu".equals("Jambu"));

System.out.println("ignore case \"jambu\" = \"Jambu\"? "

+ "jambu".equalsIgnoreCase("Jambu"));

}

}

Berikut adalah hasil keluarannya :

Operator Aritmatika

+ Operator penjumlahan (juga sebagai penyambung string)

- Operator pengurangan

* Operator perkalian

/ Operator pembagian

Page 32: Java With Eclipse

% Operator sisa pembagian

Operator aritmatika digunakan untuk melakukan operasi matematika, seperti penambahan,

pengurangan, pembagian, dan modulo (atau sisa pembagian). Contoh penggunaan :

Simbol Nama operator Contoh penggunaan

+ Operator penjumlahan n = n + 1;

- Operator pengurangan n = n - 1;

* Operator perkalian n = n * 1;

/ Operator pembagian n = n / 1;

% Operator sisa pembagian n = n % 1;

+ Operator penyambung string n = "saya "+"tidur";

Contoh kode program yang dapat Anda unduh dalam bentuk zip file atau melalui SVN di

alamat berikut : http://belajarjava.googlecode.com/svn/trunk/OperatorAritmatika

Lihat cara mengimpor contoh-contoh program ke dalam Eclipse di Bab II - Instalasi.

package operatoraritmatika;

public class OperatorAritmatika {

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

int x = 2;

int y = 5;

int z = 3;

int g = 0;

g = x + y;

System.out.println("Penjumlahan (x+y) : " + g);

g = y - x;

System.out.println("Pengurangan (y-x) : " + g);

g = x * y;

System.out.println("Perkalian (x*y) : " + g);

Page 33: Java With Eclipse

g = y / x;

System.out.println("Pembagian (y/x) : " + g);

g = z % y;

System.out.println("Sisa pembagian (z%x) : " + g);

g = x + (y * (z/x));

System.out.println("Hasilnya sekarang : " + g);

}

}

Keluaran Program :

Operator Tunggal

+ Operator plus; menyatakan nilai positif (setiap angka tanpa tanda ini akan dianggap

sebagai positif)

- Operator minus; menyatakan nilai negatif, dapat pula digunakan untuk menegatifkan

suatu bilangan

Page 34: Java With Eclipse

++ Operator kenaikan; menambah suatu bilangan dengan 1

-- Operator penurunan; mengurangkan suatu bilangan dengan 1

! Operator lawan; membalik nilai suatu boolean

Operator tunggal hanya membutuhkan satu operan untuk melakukan operasinya. Operator

ini tidak dapat digunakan untuk variabel final, karena variabel final berupa konstanta yang

tidak dapat diubah-ubah. Beberapa jenis operator tunggal diberikan pada tabel di bawah

ini.

SimbolNama

operatorOperasi Contoh

+ Operator plus menyatakan nilai positif angka = +1;

-Operator

minus

menyatakan nilai negatif, dapat pula

digunakan untuk menegatifkan suatu

bilangan

angka = -1;

angka = -angka; //

sekarang angka

bernilai 1

++Operator

kenaikanmenambah suatu bilangan dengan 1 angka = ++angka;

--Operator

penurunanmengurangkan suatu bilangan dengan 1 angka = --angka;

!Operator

lawanmembalik nilai suatu boolean ok = !true;

Operator kenaikan dan penurunan dapat diletakkan di belakang atau di depan suatu

variabel. Jika diletakkan di depan (++x atau --x), penambahan/pengurangan dilakukan

sebelumnya, sedangkan apabila diletakkan di akhir (x++ atau x--)

penambahan/pengurangan dilakukan setelahnya. Walau bagaimanapun pada akhirnya

keduanya akan menghasilkan x = x+1 atau x = x-1.

Mari kita lihat contohnya untuk membedakan lebih jelas perbedaan penempatan operator

tunggal ++ dan --

Contoh kode program yang dapat Anda unduh dalam bentuk zip file atau melalui SVN di

alamat berikut : http://belajarjava.googlecode.com/svn/trunk/OperatorTunggal

Lihat cara mengimpor contoh-contoh program ke dalam Eclipse di Bab II - Instalasi.

package operatortunggal;

public class OperatorTunggal {

Page 35: Java With Eclipse

/**

* Contoh program menggunakan operator tunggal

*

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

int x = 0;

int y = 0;

y = ++x;

System.out.println("Contoh operator pada prefix

(awalan)");

System.out.println("---------------------------");

System.out.println("Nilai x baru : " + x);

System.out.println("Nilai y = ++x : " + y);

x = 0;

y = 0;

y = x++;

System.out.println("\nContoh operator pada sufix

(akhiran)");

System.out.println("---------------------------");

System.out.println("Nilai x baru :" + x);

System.out.println("Nilai y = x++ :" + y);

}

}

Jalankan program tersebut dan lihat hasilnya. Apabila operator ++ diletakkan di awal

(prefix), maka nilai "x" dan "y" akan sama, karena penambahan nilai "x" dilakukan terlebih

dahulu, lalu hasilnya diberi kepada "y".

Apabila operator ++ diletakkan di akhir (sufix), nilai "y" adalah nilai "x" terdahulu. Java

akan memberi nilai "y" dengan nilai "x" sebelum operasi ++ dilakukan. Baru kemudian

nilai "x" ditambahkan.

Berikut ini adalah screenshot keluarannya :

Page 36: Java With Eclipse

Operator Pembanding, Boolean dan Kondisi

Operator kondisi (conditional operator) menghasilkan nilai true atau false tergantung dari

variabelnya, dalam hal ini operasinya dilakukan pada dua operand. Operator boolean

adalah operator kondisi yang kedua operandnya berupa nilai boolean (true atau false),

sedangkan Operator Pembanding membandingkan 2 nilai seperti pada operasi matematika.

Catatan : Pada objek, seperti String, operasi pembanding akan membandingkan alamat

memory tempat objek itu disimpan, bukan membandingkan isinya. Untuk membandingkan

isi String, gunakan equals(), equalsIgnoreCase(), dan compareTo() seperti dibahas

pada bagian sebelumnya.

Simbol Nama operator Contoh

== Sama dengan b = (1 == 2);

!= Tidak sama dengan b = (1 == 2);

> Lebih besar b = (1 > 2);

Page 37: Java With Eclipse

>= Lebih besar atau sama dengan b = (1 >= 2);

< Lebih kecil b = (1 < 2);

<= Lebih kecil atau sama dengan b = (1 <= 2);

&& Conditional AND b = true && false;

|| Conditional OR b = true || false;

! NOT b = !true;

?: Bentuk pendek dari if-then-else

Operator boolean AND

Operator kondisi AND menghasilkan "true" apabila kedua operandnya bernilai "true". Jika

salah satunya atau keduanya "false", operator ini menghasilkan "false". Berikut ini tabel

kebenaran operasi AND.

Op1 atau Exp1 Op2 atau Exp2 Hasil

true true true

true false false

false true false

false false false

Operator && akan mengevaluasi Op2 (di sebelah kanan) HANYA jika operand 1 bernilai

"true". Seperti terlihat dari tabel di atas, apabila Op1 (di sebelah kiri) bernilai "false",

hasilnya akan selalu "false" tidak tergantung pada isi dari Op2.

Operator kondisi OR

Operator kondisi OR menghasilkan "true" jika salah satu operandnya bernilai "true". Jika

keduanya "false", operator ini menghasilkan "false". Berikut ini table kebenaran oeprasi

OR.

Op1 atau Exp1 Op2 atau Exp2 Hasil

true true true

true false true

false true true

false false false

Page 38: Java With Eclipse

Operator || akan mengevaluasi Op2 (di sebelah kanan) HANYA jika operand 1 bernilai

"false". Seperti terlihat dari tabel di atas, apabila Op1 (di sebelah kiri) bernilai "true",

hasilnya akan selalu "true" tidak tergantung pada isi dari Op2.

Operator NOT

Operator NOT ("!") melakukan operasi boolean NOT pada operand atau ekspresi tunggal.

Operator ini mengecek nilai boolean dari suatu operand atau expresi kemudian membalik

nilainya (dari true ke false atau false ke true). Berikut ini adalah tabel kebenaran operator

NOT.

Op1 Hasil

true false

false true

Operator Ternary (?:) Java memiliki operator berkondisi lain yang disebut ternary "?:",

yang pada dasarnya merupakan bentuk pendek dari if-then-else. Secara umum

kondisi ? jika_benar : jika_salah

Pada dasarnya operator "?:" akan mengevaluasi "kondisi". Apabila kondisi bernilai "true",

operator akan mengembalikan "jika_benar", tetapi apabila "kondisi" bernilai "false",

operator akan mengembalika "jika_salah". Misalnya

x = (1 > 2) ? 10 : 20

Dari contoh di atas, "x" akan memiliki nilai 20, karena ekspresi (1 > 2) adalah salah (atau

"false").

Contoh kode program yang dapat Anda unduh dalam bentuk zip file atau melalui SVN di

alamat berikut : http://belajarjava.googlecode.com/svn/trunk/OperatorKondisi

package operatorkondisi;

public class OperatorKondisi {

/**

* Contoh program menggunakan operator kondisi

*

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

int x = 5;

int y = 10, angka = 0;

Page 39: Java With Eclipse

boolean bl = true;

if((x == 5) && (x < y))

System.out.println("Nilai x adalah " + x);

if((x == y) || (y > 1))

System.out.println("Nilai y lebih besar dari x");

angka = bl ? x : y;

System.out.println("Angka yang keluar adalah " + angka);

}

}

Berikut adalah hasil keluarannya :

Operator Pemberi Nilai & Type Casting

Operator merupakan simbol untuk melakukan perhitungan atau suatu operasi pada satu

atau lebih dari satu hal. Setelah kita mendeklarasi dan mengisi suatu variabel, kita dapat

menggunakan operator untuk melakukan beberapa hal seperti penambahan, pengurangan

dan sebagainya.

Operator Pemberi Nilai

Page 40: Java With Eclipse

= Operator pemberi nilai Operator penugasan dan pemberian nilai merupakan operator

yang paling umum dalam semua bahasa pemrograman. Operator ini dilambangkan dengan

"=" yang digunakan untuk memberikan nilai kepada suatu variabel di sebelah kiri operator.

Jika variabel tersebut telah memiliki nilai, nilainya akan diganti dengan variabel atau

angka di sebelah kanan operator.

Operator ini juga dapat digunakan untuk memberikan referensi kepada suatu objek.

Beberapa contoh :

String nama = "Budi";

boolean dewasa = true;

// buat objek baru

Shape s1 = new Shape();

// beri referensi s1 kepada s2

Shape s2 = s1;

// nilai sebelumnya diganti

dewasa = false;

Anda dapat juga memberikan nilai kepada beberapa variabel secara bersamaan. Misalnya :

x = y = z = 2;

x = (y + z);

Operator "=" dievaluasi dari kanan ke kiri. Pada ekspresi pertama, angka 2 diberikan

kepada "z", kemudian nilai yang disimpan "z" diberikan kepada "y", dan kemudian "y" ke

"x" secara bersamaan. Pada ekspresi kedua, nilai hasil evaluasi pertambahan dimasukkan

ke variabel "x".

Pada dasarnya, kedua sisi tanda "=" harus memiliki tipe yang sama. Misalnya x = A, x dan

A harus memiliki tipe yang sama. Bagaimana jika tidak, misalnya

int A;

short B;

A = 17;

B = (short)A;

Pada kode di atas, B akan mengambil nilai A yang sudah dikonversi ke tipe short. Tanda

(short) dalam kurung artinya menginstruksikan Java untuk mengubah nilai A menjadi short

sebelum diberikan kepada B.

Perubahan tipe ini disebut type casting. Kita bisa mengubah tipe apa saja, tetapi karena

rentang tiap tipe berbeda, maka nilainya belum tentu sama dengan nilai aslinya. Misalnya

(short)100000 adalah -31072.

Page 41: Java With Eclipse

Untuk perubahan tipe dari bilangan real (float atau double) ke bilangan bulat, Java

melakukan type casting dengan memangkas desimalnya. Jadi 3.56 akan diubah menjadi 3

dalam bilangan bulat.

Nilai char juga bisa diubah ke dalam bilangan bulat, yaitu dengan menggantinya dengan

bilangan Unicode-nya.

Selain operasi "=", Java juga memiliki operator pemberian nilai beruntun, dan dapat

digunakan oleh semua operator aritmatika, manipulasi bit, atau pergeseran bit. Misalnya

pada ekspresi berikut, nilai "x" akan dijumlahkan dengan 2, kemudian hasilnya disimpan

kembali ke "x".

x += 2;

Table berikut menunjukkan semua operator pemberian nilai yang dapat Anda gunakan

sehingga kode Anda menjadi lebih efisien dan mudah dibaca.

Operator Contoh Ekspresi setara

+= x += y; x = (x + y);

-= x -= y; x = (x - y);

*= x *= y; x = (x * y);

/= x /= y; x = (x / y);

%= x %= y; x = (x % y);

&= x &= y; x = (x & y);

|= x |= y; x = (x | y);

^= x ^= y; x = (x ^ y);

< <= x < <= y; x = (x < < y);

>>= x >>= y; x = (x >> y);

>>>= x >>>= y; x = (x >>> y);

Contoh kode program yang dapat Anda unduh dalam bentuk zip file atau melalui SVN di

alamat berikut : http://belajarjava.googlecode.com/svn/trunk/OperatorPemberiNilai

package operatorpemberinilai;

public class OperatorPemberiNilai {

/**

Page 42: Java With Eclipse

* Contoh program menggunakan operator pemberi nilai (assignment

operator)

*

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

int x = 5;

int y = 10;

x += y;

System.out.println("Hasil penjumlahan : " + x);

x -= y;

System.out.println("Hasil pengurangan : " + x);

x *= y;

System.out.println("Hasil perkalian : " + x);

x /= y;

System.out.println("Hasil pembagian : " + x);

x %= y;

System.out.println("Sisa pembagian : " + x);

x &= y;

System.out.println("Hasil operasi AND : " + x);

x |= y;

System.out.println("Hasil operasi OR : " + x);

x <<= y;

System.out.println("Hasil operasi pergeseran bit ke kiri

:"+ x);

}

}

Keluaran programnya :

Page 43: Java With Eclipse

Intermezzo : Membaca Input dari User

Java bukan bahasa pemrograman untuk Console (seperti DOS atau Linux), sehingga untuk

mengambil input dari user diperlukan sedikit trik yang tidak sesederhana readln pada

bahasa pemrograman lain.

Di sini saya akan menjelaskan untuk membuat program yang bisa mengambil input dari

konsol teks. Di bagian lain, setelah kita belajar pemrograman berorientasi objek, kita akan

menggunakan GUI untuk mendapatkan interaksi dari user.

Membaca String yang diketik oleh user di konsol

Kita membutuhkan kelas yang beberapa kelas, yaitu BufferedReader,

InputStreamReader, dan System.in (lawan dari System.out yang kita gunakan untuk

menampilkan pesan di layar).

Dua kelas pertama terdapat dalam package yang dinamakan java.io. Untuk itu, kita harus

menambah satu baris perintah

import java.io.*

yang berarti mengimport semua kelas dalam paket java.io (tanda * berarti semua).

Page 44: Java With Eclipse

Kemudian kita harus juga membuat suatu objek dari kelas BufferedReader. Kelas

BufferedReader adalah kelas abstrak yang menangani baca tulis ke suatu media.

Kelas ini membutuhkan kelas lain sebagai pekerjanya, yaitu InputStreamReader. Dan

InputStreamReader membutuhkan media tempat baca tulis dilakukan, yaitu System.in.

Semua ini bisa dituliskan dalam satu perintah yaitu :

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

Di sini variabel br merupakan objek yang merupakan jelmaan dari kelas BufferedReader.

Untuk memerintahkan Java mengambil input dari user, kita gunakan fungsi readline()

yang terdapat pada kelas BufferedReader, dalam hal ini terealisasi pada objek br.

nama = br.readLine();

Karena kita berhubungan langsung dengan sistem IO (input-output) komputer yang harus

diasumsikan tidak pasti (misalnya ada masalah pada sistem keyboard, atau komputer

sedang bekerja berat sehingga input dari user tidak bisa diambil), kita harus menempatkan

fungsi readLine() pada klausa

try {

...

} catch (IOException ioe) {

...

}

Perintah di dalam try { ... } adalah perintah yang kita ingin jalankan pada situasi yang

"mungkin" tidak berhasil.

Parameter pada catch, yaitu IOException ioe adalah jenis kesalahan yang ingin kita

tangkap. Dalam hal ini kita ingin menangkap adanya kesalahan IO, yaitu kesalahan yang

bertipe IOException.

Perintah di dalam catch { ... } adalah perintah yang akan dilakukan apabila kesalahan

ditangkap. Jika tidak ada kesalahan IO yang ditemukan, maka bagian ini akan dilewatkan

(tidak dijalankan).

Mari kita lihat program akhir untuk mengambil input dari user.

package ambilinputkonsol;

import java.io.*;

public class AmbilInputKonsol {

/**

* @param args

Page 45: Java With Eclipse

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

System.out.print("Masukkan nama Anda : ");

// buat objek baru untuk mengambil input

BufferedReader br = new BufferedReader(new

InputStreamReader(System.in));

// tempat di mana input dari user akan diletakkan

String nama = null;

try {

nama = br.readLine();

} catch(IOException ioe) {

System.out.println("Kesalahan IO pada saat

menanyakan nama Anda");

System.exit(1);

}

System.out.println("Terima kasih, " + nama);

}

}

Program tersebut dapat Anda unduh dalam bentuk zip file atau melalui SVN di

http://belajarjava.googlecode.com/svn/trunk/AmbilInputKonsol

Untuk menguji program tersebut, jalankan "Run -> Run" atau Ctrl-F11. Kemudian

arahkan kursor Anda ke bagian bawah di kotak yang bernama Console.

Page 46: Java With Eclipse

Masukkan nama Anda, kemudian hasilnya akan ditampilkan pada baris berikutnya.

Intermezzo : Mengubah String ke int dan double

Kita dapat mengubah tipe data String, misalnya hasil input dari konsol atau dari hasil

bacaan file, ke tipe data bilangan untuk pengolahan lebih lanjut.

Sebagai contoh, kita akan membuat program untuk menghitung nilai investasi setelah n

tahun. Input yang diminta dari user adalah :

Nama

Investasi awal (Rp)

Bunga (%)

Periode (tahun)

Page 47: Java With Eclipse

Dengan menggunakan teknik yang sama pada contoh sebelumnya, kita bisa mengambil

input tersebut menggunakan kelas BufferedReader seperti berikut.

nama = br.readLine();

strawal = br.readLine();

strbunga = br.readLine();

strperiode = br.readLine();

Tetapi karena keluaran dari readLine() bertipe String, kita harus mengubahnya menjadi

bentuk bilangan agar dapat diproses lebih lanjut.

Untuk mengubah String menjadi int, kita dapat menggunakan kelas Java Integer yang di

dalamnya memiliki fungsi parseInt(str). Fungsi ini dapat dipanggil dengan:

periode = Integer.parseInt(strperiode);

Sedangkan untuk mengubah String menjadi double, kita menggunakan kelas Java Double

yang di dalamnya memiliki fungsi parseDouble(str). Fungsi ini dapat dipanggil dengan:

awal = Double.parseDouble(strawal);

bunga = Double.parseDouble(strbunga);

Setelah semua variabel didapat dan diubah, kita baru bisa untuk memulai perhitungan.

Untuk menghitung bunga bank setelah n tahun, kita bisa menggunakan rumus berikut :

akhir = awal * (1 + bunga)periode

Dalam Java, rumus tersebut bisa dituliskan dengan ekspresi berikut

akhir = awal * Math.pow(1 + bunga,periode);

Berikut ini adalah listing program lengkap yang dapat diunduh dalam bentuk zip file atau

melalui SVN di alamat

http://belajarjava.googlecode.com/svn/trunk/HitungInvestasiNPeriode

package hitunginvestasinperiode;

import java.io.*;

public class HitungInvestasiNPeriode {

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

// buat objek baru untuk mengambil input

BufferedReader br = new BufferedReader(new

InputStreamReader(System.in));

Page 48: Java With Eclipse

// tempat di mana input dari user akan diletakkan

String nama = null;

String strawal = null;

String strbunga = null;

String strperiode = null;

try {

System.out.print("Masukkan nama Anda : ");

nama = br.readLine();

System.out.print("Masukkan jumlah awal investasi

(Rp) : ");

strawal = br.readLine();

System.out.print("Masukkan bunga (mis 0.1 = 10%) :

");

strbunga = br.readLine();

System.out.print("Masukkan periode (tahun) : ");

strperiode = br.readLine();

} catch(IOException ioe) {

System.out.println("Kesalahan IO pada saat

menanyakan nama Anda");

System.exit(1);

}

// deklarasi variabel tipe bilangan

double awal;

double bunga;

int periode;

double akhir;

// mengubah input dari Sting ke tipe data bilangan

periode = Integer.parseInt(strperiode);

awal = Double.parseDouble(strawal);

bunga = Double.parseDouble(strbunga);

// menghitung hasil investasi selama n periode

akhir = awal * Math.pow(1+bunga, periode);

Page 49: Java With Eclipse

// tampilkan hasil perhitungan di layar

System.out.println();

System.out.println("Terima kasih, " + nama);

System.out.print("Hasil investasi Anda setelah " +

periode + " tahun ");

System.out.println("adalah Rp. " + akhir);

}

}

Berikut ini adalah hasil keluarannya :

Bab IV - Memulai Java Lebih Lanjut - Struktur Kontrol

Komponen mendasar dari suatu program -- variabel, ekspresi, penyataan, dan pemanggilan

subrutin -- telah dijelaskan pada Bab sebelumnya. Mulai dari bab ini, kita akan melihat

bagaimana komponen dasar tersebut berinteraksi sehingga menjadi program yang lebih

kompleks.

Pada bab ini, kompleksitas program akan lebih dititikberatkan pada apa yang bisa

dilakukan dari dalam sebuah subrutin. Lebih khusus, akan dijelaskan lebih lanjut tentang

struktur kontrol.

Struktur kontrol, yaitu perulangan (loop) dan percabangan (branch), dapat digunakan untuk

mengulangi perintah berulang-ulang atau untuk memilih dua atau lebih skenario. Java

memiliki beberapa struktur kontrol dan kita akan lihat secara lebih detail kemudian.

Bab ini juga akan membahas tentang desain suatu program. Jika kita dihadapkan pada

suatu permasalahan yang akan dipecahkan oleh program komputer, bagaimana caranya

kita berfikir untuk merancang program tersebut. Kita akan melihat sebagian jawabannya di

bab ini, dan kemudian akan kita gunakan dalam beberapa contoh yang tersedia.

Blok, Perulangan, dan Percabangan

Kemampuan suatu program untuk melakukan tugas kompleks dilakukan dengan

menggabungkan perintah sederhana menjadi struktur kontrol. Dalam bahasa Java, ada 6

Page 50: Java With Eclipse

struktur tersebut, yaitu blok, perulangan while, perulangan do ... while, perulangan

for, pernyataan if, dan pernyataan switch.

Masing-masing struktur ini sebetulnya merupakan pernyataan tunggal yang berdiri dengan

sendirinya, tetapi di dalamnya terdiri dari satu atau lebih perintah sehingga keseluruhannya

menjadi suatu struktur perintah.

Blok

Blok adalah pernyataan sederhana yang dimulai dengan { dan diakhiri dengan }.

Tujuannya untuk mengelompokkan beberapa perintah sehingga lebih dimengerti. Misalnya

{

perintah

}

Pernyataan blok biasanya terdapat dalam struktur kontrol, tetapi sebetulnya tanpa struktur

kontrol pun, blok tetap berlaku. Contoh sederhana penggunaan blok ini adalah dalam

subrutin main() yang kita lakukan pada contoh-contoh sebelumnya.

Berikut ini adalah 2 contoh penggunaan blok.

{

System.out.println("Hallo");

System.out.println("Dunia");

}

{ // blok ini digunakan untuk menukar isi variable x dan y

int temp;

temp = x;

x = y;

y = temp;

}

Pada blok kedua, kita mendeklarasikan variable baru temp. Dalam blok kedua ini, variabel

apapun yang dideklarasikan tidak akan dapat diakses dari luar blok ini. Variabel ini disebut

variabel lokal, karena hanya bisa diakses dari dalam blok.

Ketika komputer menjalankan program ini, komputer akan mempersiapkan memori untuk

menyimpan variabel yang dideklarasikan di dalam blok ini. Ketika blok ini selesai

dijalankan, komputer akan melepas memori yang digunakan dalam blok ini untuk

digunakan oleh bagian program lain.

Konsep "scope" atau jangkauan pada konsep pemrograman, mengacu pada bisa atau

tidaknya suatu variabel dilihat oleh bagian program lain. Jangkauan suatu variabel

Page 51: Java With Eclipse

menyatakan di bagian mana variabel ini valid atau dengan kata lain bisa diakses.

Jangkauan variabel dalam suatu blok terbatas hanya pada blok tersebut.

Blok sendiri sebetulnya bukan merupakan struktur kontrol. Program akan berjalan seperti

biasa dari atas kebawah seperti biasa. Secara umum struktur kontrol dibagi menjadi 2

bagian, yaitu : perulangan dan percabangan.

Perulangan

Sebagai contoh untuk membahas tentang perulangan, kita lihat pernyataan while, yang

memiliki bentuk seperti

while (suatu_kondisi) {

perintah

}

Semantik pada pernyataan while tersebut adalah sebagai berikut. Ketika komputer sampai

pada pernyataan while, komputer akan melakukan perhitungan pada suatu_kondisi.

Apabila suatu_kondisi bernilai true, blok yang berisi perintah akan dilakukan. Setelah

komputer sampai pada bagian akhir blok, komputer akan menghitung kembali

suatu_kondisi. Jika masih bernilai true, maka blok tersebut akan dieksekusi, dan jika

tidak, program akan melompat ke bagian program berikutnya setelah blok berakhir. Atau

dengan kata lain, jika suatu_kondisi bernilai false, blok perintah tidak akan dieksekusi.

Berikut ini adalah contoh untuk menulis bilangan 1 hingga 5.

int angka = 1; // inisialisasi variabel, kita mulai dengan mengisi

variabel angka dengan 1

while (angka <= 5) {

System.out.println(angka); // cetak isi angka di layar

angka = angka + 1; // tambah angka dengan 1

}

System.out.println("Selesai...");

Variabel angka kita inisialiasi (persiapkan) dan kita isi mula-mula dengan 1. Ketika

program sampai pada pernyataan while, program akan mengevaluasi apakah angka <= 5.

Pada saat program baru dimulai, angka masih bernilai 1, sehingga pernyataan angka <= 5

bernilai true. Dalam hal ini perintah di dalam blok akan dieksekusi, yaitu mencetak angka

ke layar, kemudian menambah angka dengan 1. Sekarang angka bernilai 2.

Setelah sampai pada akhir blok, program akan kembali pada awal pernyataan while.

Sekarang angka bernilai 2, dan karena 2 masih kurang dari atau sama dengan 5, program

akan kembali mengeksekusi perintah dalam blok. Begitu seterusnya hingga angka bernilai

Page 52: Java With Eclipse

6. Pada saat ini program akan berhenti melakukan perulangan dan berhenti melakukan

eksekusi perintah di dalam blok tersebut, kemudian melakukan perintah berikutnya, yaitu

menampilkan kata "Selesai...".

Percabangan

Pernyataan if memperintahkan komputer untuk memilih salah satu aksi yang akan

dilakukan, tergantung pada suatu kondisi tertentu. Bentuknya dapat ditulis sebagai berikut

if (suatu_kondisi)

perintah_1;

else

perintah_2;

Perintah_1 dan perintah_2 juga bisa berbentuk blok, sehingga pernyataan di atas dapat

ditulis juga sebagai berikut

if (suatu_kondisi) {

perintah_1;

} else {

perintah_2;

}

Ketika komputer sampai pada pernyataan if, komputer akan menghitung apakah

suatu_kondisi bernilai true. Jika iya, maka blok perintah berikutnya akan dieksekusi,

dalam hal ini perintah_1. Jika tidak, maka blok setelah pernyataan else akan dieksekusi,

yaitu perintah_2.

Sebagai contoh, mari kita kembali pada contoh program untuk membalik nilai x dan y,

dengan syarat x harus lebih besar dari y. Dalam hal ini, setelah program ini dieksekusi,

nilai x akan selalu bernilai lebih kecil dari y, karena jika nilai x lebih besar, nilai x akan

ditukar dengan nilai y.

if (x > y) { // jika x lebih besar dari y

// blok ini digunakan untuk menukar isi variable x dan y

int temp;

temp = x;

x = y;

y = temp;

}

Contoh berikut adalah program untuk menentukan apakah suatu bilangan merupakan

bilangan genap atau bilangan ganjil. Dengan menggunakan operator %, yaitu sisa

pembagian, kita dapat menentukan apabila sisa pembagian suatu bilangan dengan 2 adalah

Page 53: Java With Eclipse

0, maka bilangan tersebut merupakan bilangan genap. Jika tidak, maka bilangan tersebut

adalah bilangan ganjil.

if ((x % 2) == 0) {

System.out.println(x + " adalah bilangan genap");

} else {

System.out.println(x + " adalah bilangan ganjil");

}

Kita akan bahas tentang struktur kontrol di bagian berikutnya. Semoga bagian ini yang

merupakan bagian pendahuluan tentang struktur kontrol dapat dimengerti sehingga kita

bisa mempelajari konsep yang lebih kompleks lagi

Perancangan Algoritma

Komputer itu bodoh! Kenapa? Karena sebagai programmer, kita harus memberikan

perintah hingga sangat detail apa yang harus dikerjakan oleh komputer. Programmer

adalah orang yang bertugas untuk menerjemahkan suatu tugas menjadi instruksi detail

yang dapat dimengerti oleh komputer. Komputer hanya melakukan apa yang diperintahkan

baris demi baris, tetapi komputer tidak bisa berfikir bagaimana melakukan suatu tugas

seefisien mungkin.

Untuk itu programmer baru harus dilengkapi dengan cara berfikir dan peralatan yang

memungkinkan mereka untuk sukses dalam menerjemahkan suatu tugas menjadi rangkaian

perintah yang bisa dimengerti oleh komputer.

Program komputer itu seperti seni yang memuat suatu ide. Seorang programmer mulai

dengan suatu tugas di kepalanya, misalnya menghitung sisi miring dari segitiga siku-siku.

Kemudian dia akan berfikir tentang apa yang harus dilakukan untuk menyelesaikan tugas

tersebut dalam bahasa manusia. Dalam hal ini misalnya, sisi miring dapat dihitung dengan

mengambil akar kuadrat dari jumlah kuadrat sisi siku-sikunya. Pemecahan masalah ini

kemudian diterjemahkan ke dalam bahasa pemrograman yang berupa perintah langkah

demi langkah bagaimana komputer harus menyelesaikan tugas tersebut.

Perintah langkah demi langkah hingga detail ini disebut algoritma. (Secara teknis,

algoritma adalah kumpulan langkah-langkah sederhana yang jelas, tidak membingungkan

karena hanya ada satu cara untuk melakukan langkah sederhana tersebut, dilakukan selama

kurun waktu tertentu. Kita tidak ingin program menghitung selamanya tanpa batas waktu.)

Program ditulis dalam bahasa pemgrograman tertentu. Tetapi algoritma ditulis secara

umum atau generic, dalam bahasa manusia, sehingga bisa diimplementasikan

menggunakan bahasa pemrograman apapun. Atau dengan kata lain, algoritma mirip seperti

Page 54: Java With Eclipse

ide di belakang program yang akan kita tulis. Tetapi ide tersebut harus jelas, dan memuat

langkah demi langkah yang sederhana.

Jadi darimana algoritma itu datang? Biasanya orang harus membuat algoritma itu. Dengan

skill, latihan dan pengalaman, orang akan lebih mudah membuat suatu algoritma.

Di bagian ini akan dibahas tentang bagaimana membuat suatu algoritma. Jadi bahasa Java

akan kita tinggalkan sementara. Kita akan beralih untuk berfikir secara abstrak.

Misalnya, kita memiliki suatu tugas di kepala. Salah satu cara untuk menyelesaikan tugas

itu adalah menuliskan penyelesaiannya langkah demi langkah, dan dari sana kita membuat

algoritma untuk menyelesaikan masalah itu. Kemudian dari setiap langkah tersebut, kita

bisa membuat langkah-langkah lain yang lebih detail, sampai kita bisa menerjemahkan

langkah-langkah itu ke dalam bahasa pemrograman. Metode ini disebut penghalusan

bertahap (stepwise refinement), dan sifatnya top-down atau dari atas ke bawah. Sambil kita

menambah detail pada setiap langkah, kita bisa mulai menuliskan algoritma dalam bentuk

pseudocode (kode palsu) yang bentuknya tidak harus persis atau mengikuti suatu bahasa

pemrograman.

Misalnya, kita ambil contoh untuk menghitung rata-rata dari 5 bilangan. Kita bisa

menuliskan pseudocode dalam bentuk berikut :

ambil input user untuk bilangan pertama

masukkan ke variabel x

ambil input user untuk bilangan kedua

tambahkan variabel x dengan bilangan kedua

ambil input user untuk bilangan ketiga

tambahkan variabel x dengan bilangan ketiga

ambil input user untuk bilangan keempat

tambahkan variabel x dengan bilangan keempat

ambil input user untuk bilangan kelima

tambahkan variabel x dengan bilangan kelima

bagi variabel x dengan 5

tampilkan hasilnya di layar

Algoritma di atas betul, tetapi terlalu banyak perulangan. Bagaimana jika bilangan yang

akan dirata-ratakan ada 100? Kita bisa udah algoritma di atas dengan pseudocode yang

lebih mudah dimengerti, misalnya

while a kurang dari 5

ambil input user untuk bilangan ke-a

tambahkan nilai x dengan bilangan ke-a

Page 55: Java With Eclipse

tambah a dengan 1

bagi variabel x dengan 5

tampilkan di layar

Sekarang, ambil input user bisa dideskripsikan lebih jauh. Kita harus memberikan

pertanyaan kepada user. Dan seperti dipelajari pada bab sebelumnya, input dari user berupa

String yang harus diterjemahkan ke dalam tipe data bilangan untuk bisa dikalkulasi.

Pseudocode di atas dapat ditulis ulang sebagai berikut :

while a kurang dari 5

beri pertanyaan kepada user untuk memasukkan bilangan ke-a

ambil input user untuk bilangan ke-a

ubah input menjadi bilangan

tambahkan nilai x dengan bilangan ke-a

tambah a dengan 1

bagi variabel x dengan 5

tampilkan di layar

Dan juga dari pelajaran sebelumnya, untuk mengambil input user, kita bisa dihadapkan

pada permasalahan IO (input output). Selain itu kita juga harus menginisialisasi pembaca

dan tempat meletakkan string sementara. Untuk itu, kita harus mengubah pseudocode nya

menjadi seperti ini

inisialisasi pembaca

inisialisasi tempat string sementara

while a kurang dari 5

beri pertanyaan kepada user untuk memasukkan bilangan ke-a

"coba" ambil input user untuk bilangan ke-a

jika ada masalah keluar dari program

ubah input menjadi bilangan

tambahkan nilai x dengan bilangan ke-a

tambah a dengan 1

bagi variabel x dengan 5

tampilkan di layar

Kemudian, perlu diperhatikan bahwa variabel a dan x tidak diketahui nilai awalnya oleh

komputer. Bayangkan jika nilai a dan x ditentukan oleh komputer secara acak, bukan saja

hasil rata-ratanya menjadi tak tentu, tetapi juga perulangannya menjadi tak menentu. Untuk

itu kita harus tambahkan perintah untuk menginisialisasi a dan x dengan 0, sehingga

pseudocodenya menjadi

inisialisasi x dengan 0

inisialisasi a dengan 0

inisialisasi pembaca

Page 56: Java With Eclipse

inisialisasi tempat string sementara

while a kurang dari 5

beri pertanyaan kepada user untuk memasukkan bilangan ke-a

"coba" ambil input user untuk bilangan ke-a

jika ada masalah keluar dari program

ubah input menjadi bilangan

tambahkan nilai x dengan bilangan ke-a

tambah a dengan 1

bagi variabel x dengan 5

tampilkan di layar

Dari sini kita bisa menerjemahkan pseudocode tersebut menjadi program Java, yaitu

double x = 0;

int a = 0;

BufferedReader br = new BufferedReader(new InputStreamReader(System.in));

String strbilangan = null;

while (a < 5) {

System.out.print("Masukkan bilangan ke-" + a + " : ");

try {

strbilangan = br.readLine();

} catch (IOException ioe) {

System.out.println("Kesalahan IO, program berhenti");

System.exit(1);

}

x = x + Double.parseDouble(strbilangan);

a = a + 1;

}

x = x / 5;

System.out.println("Rata-rata bilangan yang dimasukkan adalah " + x);

Jangan lupa bahwa program tersebut membutuhkan paket java.io.*, sehingga kita harus

menambah

import java.io.*

di awal kelas.

Berikut ini adalah program lengkapnya dan dapat diunduh dalam bentuk zip file atau

melalui SVN di alamat berikut

http://belajarjava.googlecode.com/svn/trunk/HitungRataRata5Bil

package hitungratarata5bil;

Page 57: Java With Eclipse

import java.io.*;

public class HitungRataRata5Bil {

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

double x = 0;

int a = 0;

BufferedReader br = new BufferedReader(new

InputStreamReader(System.in));

String strbilangan = null;

while (a < 5) {

System.out.print("Masukkan bilangan ke-" + a + " :

");

try {

strbilangan = br.readLine();

} catch (IOException ioe) {

System.out.println("Kesalahan IO, program

berhenti");

System.exit(1);

}

x = x + Double.parseDouble(strbilangan);

a = a + 1;

}

x = x / 5;

System.out.println("Rata-rata bilangan yang dimasukkan

adalah " + x);

}

}

Untuk menguji program tersebut, jalankan "Run -> Run" atau Ctrl-F11. Kemudian

arahkan kursor Anda ke bagian bawah dan klik di kotak yang bernama Console.

Page 58: Java With Eclipse

Berikut ini adalah hasil keluarannya, Anda bisa mencoba-coba dengan bilangan apapun.

Membuat kode, menguji, dan mencari kesalahan (debugging)

Akan sangat sederhana dan indah apabila program yang kita tulis akan berjalan 100%

tanpa kesalahan sama sekali. Kita tinggal memencet tombol run, dan semuanya berjalan

sesuai rencana.

Nyatanya tidak demikian.

Coba Anda ganti salah satu dari beberapa contoh yang sudah diberikan dengan mengganti

salah satu kurung { menjadi (. Kemudian pada Eclipse, jalankan dengan "Run -> Run".

Lihat apa yang terjadi.

Page 59: Java With Eclipse

Eclipse akan menampilkan pesan kesalahan, itu artinya secara sintaks, atau format

penulisan program ada memiliki kesalahan, dan Anda harus memperbaikinya sebelum

program bisa dijalankan.

Sintaks adalah salah satu cara menentukan bagaimana komputer harus bekerja. Manusia

tidak membutuhkan sintaks untuk berbicara karena manusia bisa menentukan mana subjek,

predikat, atau objek dengan mudah, bahkan untuk kalimat yang belum pernah dibaca atau

didengar sekalipun.

Bagi komputer, sintaks membantu komputer mengelola logika, mana yang merupakan

variabel, mana yang berupa subrutin, mana perulangan, mana percabangan dan lain-lain.

Dalam bahasa Java, yang merupakan bahasa dengan sintaks ketat, perbedaan sintaks

sedikit saja membuat Java tidak mengerti apa yang dimaksud dalam program.

Bagi beberapa programmer, ketidakluwesan Java mungkin menghambat, tetapi perlu

diingat bahwa kesalahan hanya datang dari manusia. Ketidakluwesan Java membantu

programmer harus menggunakan logika yang benar, dan tidak boleh salah dalam

menggunakan variabel. Variabel yang sudah ditentukan tipenya tidak dapat diubah

ditengah jalan, kecuali dengan membuat variabel baru dengan tipe yang berbeda.

Debugging

Ketika program Anda tidak menunjukkan kesalahan sintaks, bukan berarti program Anda

bebas dari kesalahan. Program harus diuji apakah ia berjalan sesuai dengan yang

diharapkan. Idealya apabila program menerima input yang tidak sesuai dengan yang

Page 60: Java With Eclipse

diharapkan, program akan memberikan pesan kepada penggunanya tentang kesalahan

tersebut, bukan tiba-tiba keluar tanpa pesan.

Kita mungkin harus melakukan testing secara lebih perlahan-lahan. Apabila jumlah baris

dalam program kita kecil, mungkin kesalahan tersebut dengan mudah akan kita temui.

Tetapi apabila program tersebut sangat besar, maka mencari kesalahan yang kelihatannya

sederhana tidaklah mudah.

Pencarian kesalahan ini dalam bahasa pemrograman disebut dengan "debugging", yang

jika diterjemahkan ke dalam bahasa Indonesia berarti membersihkan kutu, karena

kesalahan kecil dianggap sebagai kutu yang mengganggu jalannya program.

Eclipse menyediakan peralatan untuk melakukan debugging, yaitu dengan menekan

tombol "Run -> Debug". Dalam Eclipse, debugging yang bisa dilakukan antara lain:

membuat breakpoint (tempat berhenti program), mengeksekusi satu demi satu perintah,

mengeksekusi satu subrutin, melihat isi variabel saat ini, dan bahkan mengganti variabel

pada saat program dijalankan.

Untuk mendemonstrasikan debugging ini, mari kita gunakan program menghitung rata-rata

pada bagian terdahulu.

Membuat breakpoint

Kita bisa menjalankan program baris demi baris, tetapi sebelum kita melakukannya, kita

harus memberhentikan program tersebut di satu tempat terlebih dahulu. Tempat

pemberhentian program sementara ini disebut breakpoint. Untuk mengeset breakpoint,

double klik bagian paling kiri dari baris di mana Anda ingin program tersebut berhenti.

Misalnya pada tampilan berikut, program berhenti di baris double x=0;. Perhatikan juga

bahwa baris yang diberi breakpoint memiliki tanda bulat di sampingnya.

Page 61: Java With Eclipse

Untuk menghapus breakpoint, double click lagi tanda bulat di baris yang sudah diberi

breakpoint tadi.

Menjalankan program hingga breakpoint

Coba tekan tombol F11 atau "Run -> Debug". Pada saat Anda memulai debugging,

Eclipse akan memberikan pertanyaan bahwa perspective (atau tampilan Eclipse) Anda

akan diubah ke debugging perspective. Tekan Yes untuk mengubah perspective tersebut.

Berikut ini adalah perspektif dalam mode debugging.

Page 62: Java With Eclipse

Perhatikan bahwa di samping tanda bulat tadi, ada tanda panah (->) yang menunjukkan

saat ini komputer telah melaksanakan program hingga pada baris ini. Di samping kanan

adalah daftar variabel yang dideklarasikan dalam program ini. Karena baris yang kita pilih

adalah baris pertama setelah deklarasi pada subrutin main, maka hanya satu variabel yang

tersedia, yaitu yang berasal dari arg.

Menjalankan program langkah per langkah

Apabila Anda sudah berada dalam mode debugging, Anda bisa melakukan navigasi untuk

melakukan eksekusi sesuka Anda. Ada beberapa pilhan yang disediakan oleh Eclipse,

semuanya terdapat dalam menu Run yaitu :

Resume melanjutkan eksekusi program yang berhenti.

Suspend menghentikan sementara program yang sedang jalan.

Terminate menghentikan sesi debugging saat ini.

Step Into mengeksekusi SATU perintah atau masuk ke dalam subrutin.

Step Over mengeksekusi SATU perintah. Jika perintah tersebut merupakan

perintah panggilan subrutin, maka subrutin tersebut akan dieksekusi tanpa masuk

ke dalam subrutin yang dipanggil tersebut.

Page 63: Java With Eclipse

Step Return mengeksekusi seluruh perintah pada subrutin ini kemudian kembali

kepada pemanggilnya.

Run to Line menjalankan program dari baris tempat berhentinya program sekarang

hingga baris yang Anda tentukan. Atau jika di antaranya terdapat breakpoint lagi,

maka program akan berhenti di breakpoint tersebut.

Mari kita coba untuk menjalankan progam satu langkah dengan menggunakan

"Run -> Step Over" atau tombol F6.

Berikut ini adalah screen shot setelah program maju satu langkah. Perhatikan

bahwa tanda panah maju satu langkah. Dan juga karena instruksi pada baris ini

adalah mendeklarasikan variabel x bertipe double, dan kemudian mengisinya

dengan 0, variabel ini akan muncul di kanan atas perspektif Debug.

Lakukan terus Step Over hingga program berakhir, dan Anda akan melihat langkah

demi langkah bagaimana program dijalankan secara bertahap. Perlu diingat, pada

saat program sampai pada instruksi untuk mengambil input Anda, klik bagian

bawah (tab yang bernama Console), dan masukkan angka sebagai input kemudian

tekan Enter. Lihat bagaimana variabel di kanan atas berubah sesuai dengan input

yang Anda berikan.

Page 64: Java With Eclipse

Melihat dan mengubah isi variable Di sini saya jalankan program setiap baris

hingga a = a + 1 seperti pada gambar berikut.

Kemudian, saya ingin memperpanjang perulangan, sehingga bukan melakukan 5

kali perulangan, tetapi 6 kali, yaitu dengan mengubah nilai a menjadi -1. Ini bisa

dilakukan yaitu dengan mengklik nilai dari a, yang semula 0, kemudian diganti

menjadi -1, seperti pada screen shot berikut.

Setelah diganti coba jalankan program hingga selesai, misalnya dengan "Run ->

Resume". Jangan lupa untuk memasukkan inputnya di bagian Console. Lihat

sekarang bilangan ke-0 ditanyakan 2 kali, yang artinya user diperintahkan untuk

Page 65: Java With Eclipse

memasukkan 6 bilangan. Tentunya hasil rata-ratanya salah, karena perhitungan

rata-rata dilakukan dengan rumus x = x/5, dalam hal ini seharusnya adalah 6.

Setelah selesai melakukan debugging, Anda mungkin ingin mengubah perspektif

kembali seperti semula. Caranya, di bagian paling kanan (lihat gambar dibawah),

klik tanda >> kemudian pilihan akan muncul. Klik perspektif Java untuk

mengubahnya menjadi tampilan semula.

while dan do ... while

Pernyataan while

Pernyataan while telah diperkenalkan pada bagian sebelumnya. Perulangan while

memiliki bentuk

while (suatu_kondisi)

perintah

perintah bisa juga berupa blok yang berisi kumpulan perintah-perintah di antara { dan }.

perintah ini disebut juga dengan inti perulangan. Inti perulangan akan terus dieksekusi

selama suatu_kondisi bernilai true. suatu_kondisi ini disebut juga penguji

perulangan.

Ada beberapa hal yang mungkin jadi pertanyaan. Apa yang terjadi jika suatu_kondisi

sudah bernilai false sejak pertama kali komputer sampai pada pernyataan ini? Dalam hal

ini blok perulangan tidak akan dieksekusi sama sekali. Program akan melompat ke perintah

berikutnya di luar blok perulangan.

Page 66: Java With Eclipse

Lalu apa yang terjadi jika suatu_kondisi bernilai false di tengah-tengah perulangan,

apakah program keluar dari perulangan saat itu juga? Dalam hal ini, tentunya tidak.

Program akan mengeksekusi semua perintah hingga akhir blok selesai. Setelah itu program

akan kembali ke pernyataan while, dan setelah mengevaluasi kembali suatu_kondisi, dan

jika hasilnya salah, baru program akan melompat ke perintah berikutnya di luar blok.

Mari kita ubah sedikit algoritma yang kita buat di bagian sebelumnya. Kali ini kita akan

membuat program yang menghitung rata-rata dengan cara menanyakan suatu bilangan

kepada user, kemudian program akan berhenti jika masih ada data yang akan diproses.

Inisialisasi jumlah dengan 0

Inisialisasi n (berapa data yang dimasukkan user) dengan 0

while (masih ada data yang akan diproses):

Minta input dari user

Tambah jumlah dengan input dari user

Tambah n dengan 1

Bagi jumlah dengan n untuk menghitung rata-rata

Cetak rata-rata ke layar

Pertanyaan berikutnya, bagaimana menentukan masih ada data yang akan diproses? Cara

yang paling mudah adalah melihat apakah nilai yang dimasukkan user bernilai 0. Nol di

sini bukan termasuk data yang akan dijumlahkan tetapi bertugas sebagai sinyal bahwa

tidak ada lagi data yang harus dimasukkan.

Lalu bagaimana kita harus menguji bahwa data yang dimasukkan bernilai 0 atau bukan?

(Ingat, kita baru menanyakan data di dalam blok perulangan. Pada saat komputer pertama

kali menemui perulangan while, komputer tidak tahu apa-apa.) Dalam hal ini, kita akan

sedikit ubah algoritma kita seperti berikut :

Inisialisasi jumlah dengan 0

Inisialisasi n (berapa data yang dimasukkan user) dengan 0

Minta input dari user

while (input tidak sama dengan 0):

Tambah jumlah dengan input dari user

Tambah n dengan 1

Minta input dari user

Bagi jumlah dengan n untuk menghitung rata-rata

Cetak rata-rata ke layar

Pada dasarnya, kita tanyakan user terlebih dahulu sebelum perulangan while. Dengan cara

ini, kita bisa mendapat nilai input untuk dievaluasi pada pernyataan while. Di dalam

perulangan while, kita tempatkan pertanyaan untuk mendapat input dari user di akhir

Page 67: Java With Eclipse

perulangan. Artinya, setelah kita memproses input dari user, kita akan tanyakan lagi

kepada user untuk mendapatkan kondisi untuk mengevaluasi kondisi while berikutnya.

Perhatikan juga bahwa ketika 0 dimasukkan, program tidak akan menghitung nilai 0 lagi.

Di algoritma sebelumnya, nilai 0 akan ikut dijumlahkan dan n akan bertambah 1, padahal 0

bukan data. Nol hanya berfungsi sebagai sinyal bahwa perulangan harus selesai. Masalah

ini sangat amat umum ditemui oleh programmer, karena menghitung satu demi satu

ternyata tidak mudah. Untuk itu debugger diperlukan untuk melihat lebih detail apa yang

dilakukan oleh komputer.

Kita bisa ubah algoritma di atas menjadi program Java sebagai berikut. (Seperti biasa

program ini bisa diunduh dalam bentuk zip file atau di alamat SVN berikut:

http://belajarjava.googlecode.com/svn/trunk/RataRata )

package ratarata;

import java.io.*;

public class RataRata {

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

double jumlah = 0;

double bilangan = 0;

int n = 0;

BufferedReader br = new BufferedReader(new

InputStreamReader(System.in));

String strbilangan = null;

System.out.print("Masukkan bilangan pertama : ");

try {

strbilangan = br.readLine();

} catch (IOException ioe) {

System.out.println("Kesalahan IO, program

berhenti");

System.exit(1);

}

Page 68: Java With Eclipse

// mengubah input menjadi double agar bisa diproses lebih

lanjut

bilangan = Double.parseDouble(strbilangan);

while (bilangan != 0) {

jumlah += bilangan; // sama dengan : jumlah =

jumlah + bilangan

n++; // sama dengan : n = n+1

// tanya user input

System.out.print("Masukkan bilangan berikutnya (atau

0 untuk mengakhiri) : ");

try {

strbilangan = br.readLine();

} catch (IOException ioe) {

System.out.println("Kesalahan IO, program

berhenti");

System.exit(1);

}

// mengubah input menjadi double agar bisa diproses

lebih lanjut

bilangan = Double.parseDouble(strbilangan);

}

// hitung rata-rata

double ratarata = jumlah/n;

// cetak hasilnya ke layar

if (n == 0) {

System.out.println("Data kosong, rata-rata tidak

bisa dihitung");

} else {

System.out.println("Anda memasukkan " + n + "

data");

System.out.println("Rata-rata bilangan yang

dimasukkan adalah " + ratarata);

}

}

}

Berikut hasil keluarannya

Page 69: Java With Eclipse

Pernyataan do ... while

Kadang-kadang akan lebih mudah menulis perulangan jika penguji perulangan dilakukan

di akhir badan perulangan. Dalam hal ini badan perulangan akan dieksekusi terlebih dahulu

tanpa memperdulikan apakah suatu kondisi bernilai true atau false. Pengujian dilakukan di

akhir setelah suatu kondisi didapat dalam eksekusi perulangan pertama kali.

Pernyataan do ... while pada dasarnya merupakan pernyataan while terbalik, dengan

bentuk.

do

perintah

while (suatu_kondisi);

Atau apabila perintah berbentuk blok kumpulan perintah-perintah, bisa juga ditulis dalam

bentuk

do {

perintah-perintah

} while (suatu_kondisi);

Perlu diingat bahwa pernyataan do ... while diakhiri dengan tanda ; di akhir while.

Contoh sederhana adalah program bermain game, di mana game akan menanyakan apakah

user ingin bermain lagi.

do {

main game

tanya user apakah ingin main lagi

} while (user menjawab ya);

Jika suatu saat Anda harus menulis kondisi pada pernyataan while seperti ini while

(jawaban == true), Anda bisa mengganti pernyataan ini menjadi while (jawaban).

Menguji apakah jawaban sama dengan true sama artinya dengan melihat apakah jawaban

berisi "true".

Demikian juga dengan while (jawaban == false), bisa diganti dengan while

(!jawaban). Seperti dijelaskan pada bab tentang operator boolean, operator ! membalik isi

Page 70: Java With Eclipse

dari boolean, misalnya dari true menjadi false atau sebaliknya. Dengan menuliskan while

(!jawaban) berarti sama dengan menguji apakah jawaban berisi false.

Pernyataan do ... while memberikan keleluasaan kepada Anda untuk berekspresi

dengan lebih lugas. Sebenarnya, untuk memecahkan suatu masalah dengan perulangan do

... while juga bisa diekspresikan dengan perintah while, demikian juga sebaliknya.

do

perintah

while (suatu_kondisi);

bisa dituliskan dalam bentuk

perintah

while (suatu_kondisi)

perintah

Demikian juga dengan

while (suatu_kondisi)

perintah

bisa juga dituliskan dalam bentuk

if (suatu_kondisi) {

do {

perintah

while (suatu_kondisi);

}

tanpa merubah aliran program sama sekali.

Pernyataan break dan continue

Pernyataan while dan do ... while menguji kondisi di awal atau di akhir badan

perulangan. Pengujian bisa juga dilakukan di tengah-tengah badan perulangan, kemudian

memerintahkan program untuk keluar dari badan perulangan saat itu juga. Caranya dengan

menggunakan perintah

break<code>, sehingga program seperti

<code>while (suatu_kondisi)

perintah

bisa ditulis dalam bentuk

while (true)

perintah

if (!suatu_kondisi)

break;

<code>

Page 71: Java With Eclipse

Apa makna dari program di atas? <code>while (true)

artinya memerintahkan program untuk melakukan perulangan selamanya, karena true tidak

akan berubah. Di tengah-tengah program, kita uji apakah suatu_kondisi bernilai false.

Jika ya, maka perintah break akan dieksekusi yang menyebabkan program keluar dari

badan perulangan ke perintah berikutnya di luar badan perulangan.

Kadang-kadang gaya penulisan ini lebih masuk akal ketimbang gaya penulisan baku

seperti while atau do ... while, tapi tentu saja ini tergantung dari cara pikir masing-

masing programmer dan juga masalah yang akan dipecahkan.

Pernyataan while atau do ... while dapat dibuat bertingkat, misalnya membuat blok

while dalam blok while.

while (suatu_kondisi) {

perintah

while (kondisi_lain) {

perintah_lain

while (kondisi_lain_lagi) {

perintah_baru

}

}

}

Apabila perintah break diberikan, maka program akan keluar dari perulangan yang berada

persis di atasnya. Misalnya, apabila perintah break diberikan setelah perintah_lain

maka program akan keluar dari dalam perulangan while (kondisi_lain).

Perlu diingan juga apabila perintah break diberikan di dalam pernyataan if, maka

program akan keluar dari perulangan yang persis di atasnya.

Selain perintah break yang secara langsung menghentikan perulangan, perintah continue

digunakan untuk menghentikan operasi saat itu, mengabaikan perintah hingga perulangan

berakhir, kemudian kembali kepada perintah while lagi. Misalnya,

while (suatu_kondisi) {

perintah

continue;

perintah_lagi

perintah_lain_lagi

}

Perulangan akan menjalankan perintah, mengabaikan perintah_lagi dan

perintah_lain_lagi, kemudian kembali kepada pernyataan while untuk mengevaluasi

apakah suatu_kondisi bernilai true. Jika ya perulangan akan diteruskan. Tetapi karena

Page 72: Java With Eclipse

ada perintah continue, artinya selama perulangan tersebut berjalan, perintah_lagi dan

perintah_lain_lagi tidak akan pernah dieksekusi.

Perulangan for

Kita akan membahas bentuk perulangan lain, yaitu perulangan for. Setiap bentuk

perulangan for dapat diubah menjadi bentuk perulangan while dengan fungsi yang sama

tanpa mengubah alur program. Tetapi tergantung dari permasalahan yang akan kita

pecahkan, menulis program dengan for akan membuat alur program lebih mudah

dipahami.

Misalnya, kita akan menghitung 1+2+3+4+5+...+100. Kita bisa ekspresikan program

tersebut dalam bentuk

i = 1;

jumlah = 0;

while (i <= 100) {

jumlah += i;

i++;

}

Perulangan ini dapat ditulis juga dengan

jumlah = 0;

for (i = 1; i <= 100; i++)

jumlah += i

Apa point-point penting yang kita lihat dari perubahan ini? Pertama mari kita lihat bentuk

penggunaan while yang umum dilakukan

inisialisasi variabel

while (suatu_kondisi_variabel) {

perintah

update_variabel

}

Di sini perulangan while memiliki 3 komponen penting, yaitu inisialisasi, yaitu

memberikan nilai awal suatu variabel, suatu_kondisi_variabel, yaitu pengujian bahwa

perulangan akan terus dilakukan selama kondisi ini bernilai true, dan terakhir

update_variabel, yaitu instruksi mengubah nilai kondisi variabel untuk membatasi

perulangan sehingga akan selesai suatu saat, tidak berulang terus menerus.

Pada perulangan for, ketiga komponen ini dirangkai menjadi satu dalam bentuk

for (inisialisasi_variabel; kondisi_variabel; update_variabel)

perintah

Page 73: Java With Eclipse

atau jika perintah merupakan blok yang terdiri dari banyak perintah, dapat dituliskan juga

dalam bentuk

for (inisialisasi_variabel; kondisi_variabel; update_variabel) {

banyak_perintah

}

Di sini inisialisasi variabel bisa berupa apa saja yang berbentuk perintah, misalnya

memberikan variabel dengan nilai awal tertentu, dalam bentuk variabel = nilai_awal.

kondisi_variabel harus berbentuk pernyataan boolean seperti suatu_kondisi pada

pernyataan while. Sedangkan update_variabel juga berbentuk perintah.

inisialisasi_variabel, kondisi_variabel, atau update_variabel dapat

dikosongkan dan tidak harus selalu diisi. Bagian yang tidak diisi yang selalu digantikan

dengan true, yang artinya perulangan akan terus dieksekusi tanpa henti. Untuk

menghentikannya, perintah break harus diberikan ditengah-tengah badan perulangan.

Bentuk paling umum dari perulangan for adalah menghitung. Biasanya perulangan ini

memiliki bentuk seperti

for (variabel = nilai_minimum; variabel <= nilai_maksimum; variabel++) {

perintah

}

Perhatikan bagian terakhir adalah menaikkan nilai variabel dengan 1, yang artinya

perulangan akan dimulai dengan nilai_minimum, diakhiri dengan nilai_maksimum

dengan jeda 1.

Contoh, jika kita ingin berhitung 2,5,8,11,14,17, atau dengan kata lain, mulai dari 2 hingga

17 dengan jeda 3, kita bisa mengekspresikan for dengan

for (i = 2; i <= 17; i += 3) {

System.out.println(i);

}

Untuk menghitung mundur, kita bisa menggunakan perintah seperti

for (i = 20; i >= 0; i--) {

System.out.println(i);

}

Atau jika kita ingin menghitung maju dan mundur pada saat yang bersamaan, misalnya i

dari 1 hingga 10 dan j dari 10 hingga 1, kita bisa ekspresikan dengan

for (i = 1, j = 10; i <= 10; i++, j--) {

System.out.println(i + " " + j);

}

Page 74: Java With Eclipse

Catatan penting! Variabel yang akan digunakan, dalam contoh di atas i dan j, adalah

variabel yang harus dideklarasikan sebelumnya. Java adalah bahasa pemrograman ketat,

yang artinya semua harus didefinisikan dengan jelas sebelum digunakan. Untuk banyak

kasus, deklarasi variabel dan perulangannya bisa dilakukan serentak pada bagian

inisialisasi variabel. Misalnya

for (int i = 1; i <= 10; i++) {

System.out.println(i)

}

Perhatikan ada imbuhan int di depan inisialisasi variabel i, yang merupakan deklarasi

variabel i dengan tipe data int sekaligus menginisialisasi nilainya dengan 1.

Perulangan for bertingkat

Seperti pada perulangan while, perulangan for pun dapat dilakukan bertingkat, artinya

perulangan for di dalam perulangan for.

Kita ambil contoh sederhana misalnya membuat tabel perkalian seperti

1 2 3 4 5 6 7 8 9 10 11 12

2 4 6 8 10 12 14 16 18 20 22 24

3 6 9 12 15 18 21 24 27 30 33 36

4 8 12 16 20 24 28 32 36 40 44 48

5 10 15 20 25 30 35 40 45 50 55 60

6 12 18 24 30 36 42 48 54 60 66 72

7 14 21 28 35 42 49 56 63 70 77 84

8 16 24 32 40 48 56 64 72 80 88 96

9 18 27 36 45 54 63 72 81 90 99 108

10 20 30 40 50 60 70 80 90 100 110 120

11 22 33 44 55 66 77 88 99 110 121 132

12 24 36 48 60 72 84 96 108 120 132 144

Program untuk membuat tabel perkalian tersebut bisa diekspresikan dengan algoritma

pseudocode sebagai berikut

untuk setiap baris i = 1,2,3...,12

cetak perkalian i dengan 1,2,3..12

cetak baris baru

Kalau kita jabarkan lebih lanjut, perintah kedua juga merupakan perulangan dari 1 hingga

12, sehingga algoritma di atas bisa kita tulis sebagai

untuk setiap baris i = 1,2,3...,12

untuk setiap kolom j = 1,2,3...,12

cetak i*j

cetak baris baru

Kita bisa terjemahkan ke dalam bahasa Java sebagai

Page 75: Java With Eclipse

for (int i = 1; i <= 12; i++) {

for (int j = 1; j <= 12; j++) {

System.out.print(i*j + " ");

}

System.out.println("");

}

Berikut ini adalah contoh program yang bisa diunduh dalam bentuk zip file atau dari

gudang SVN di alamat http://belajarjava.googlecode.com/svn/trunk/TabelPerkalian :

package tabelperkalian;

public class TabelPerkalian {

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

for (int i = 1; i <= 12; i++) {

for (int j = 1; j <= 12; j++) {

System.out.print(i*j + " ");

}

System.out.println("");

}

}

}

Berikut ini hasil kelurannya:

Page 76: Java With Eclipse

Pernyataan if

Pernyataan if merupakan salah satu pernyataan percabangan pada Java, dengan bentuk

umum seperti

if (suatu_kondisi)

perintah1

else

perintah2

Seperti biasa, perintah1 dan perintah2 bisa berbentuk blok yang terdiri dari beberapa

perintah. Pernyataan if merupakan bentuk percabangan 2 arah. Bagian else yang terdiri

dari kata "else" dan perintah2 tidak selalu harus ada.

Perhatikan bahwa baik perintah1 dan perintah2 bisa merupakan pernyataan if itu sendiri.

Ada beberapa hal menarik yang mungkin berguna. Ambil contoh dalam pernyataan berikut

if (x > 0)

if (y > 0)

System.out.println("perintah1");

else

System.out.println("perintah2");

Pertama-tama, komputer tidak peduli bagaimana Anda memformat paragraf dan indentasi

dari pernyataan if tersebut. Java akan menganggap else terkait dengan if terdekat,

sehingga kode di atas akan dianggap seperti

if (x > 0)

if (y > 0)

System.out.println("perintah1");

else

System.out.println("perintah2");

else di program di atas akan dianggap bagian dari pernyataan jika y > 0, padahal yang kita

maksud adalah else jika x > 0. Untuk memperbaikinya, kita tambahkan tanda kurung {}

sehingga menjadi

if (x > 0) {

if (y > 0)

System.out.println("perintah1");

}

else

System.out.println("perintah2");

Kedua pernyataan tersebut memiliki arti yang berbeda. Jika x <= 0, pada kode pertama

Java tidak mencetak apa-apa ke layar, sedangkan kode kedua java akan mencetak

"perintah2".

Page 77: Java With Eclipse

Lebih menarik lagi, perhatikan kode berikut

if (kondisi_pertama)

perintah1

else

if (kondisi_kedua)

perintah2

else

perintah3

Lagi-lagi karena Java tidak membedakan indentasi penulisan, maka kode tersebut akan

diterjemahkan Java seperti

if (kondisi_pertama)

perintah1

else if (kondisi_kedua)

perintah2

else

perintah3

Dengan kata lain perintah tersebut lebih seperti percabangan 3 arah. Komputer akan

mengeksekusi hanya salah satu dari perintah1, perintah2, atau perintah3. Komputer

akan mengevaluasi kondisi_pertama, jika true, maka perintah1 dieksekusi sementara

perintah2 dan perintah3 diabaikan. Jika false, maka kondisi_kedua akan dievaluasi.

Jika true, maka perintah2 akan dieksekusi dan perintah3 diabaikan. Jika false, maka

hanya perintah3 saja yang dieksekusi.

Berikut ini adalah contoh penggunaan percabangan 3 arah.

if (suhu < 20)

System.out.println("Dingin");

else if (suhu < 30)

System.out.println("Lumayan");

else

System.out.println("Panas");

Kita bahkan dapat membentuk pernyataan if-else ini menjadi percabangan N arah,

misalnya

if (kondisi_pertama)

perintah1

else if (kondisi_kedua)

perintah2

else if (kondisi_ketiga)

perintah3

else if (kondisi_keempat)

Page 78: Java With Eclipse

perintah4

.

.

.

else if (kondisi_keNminus1)

perintahNmin1

else

perintahN

Contoh berikut ini adalah mengurutkan 3 bilangan dari kecil ke besar. Misalnya kita

mempunyai 3 variabel a,b dan c. Bilangan yang paling kecil adalah bilangan yang lebih

kecil dari kedua bilangan yang lain. Sekarang mari kita rangkai logika untuk menentukan

urutan bilangan dari kecil ke besar. Mula-mula kita cek apakah a lebih kecil dari b dan c,

yaitu dengan pernyataan

if (a < b && a <c)

Jika a betul merupakan bilangan terkecil, maka kita uji apakah b lebih kecil dari c dengan

perintah

if (b < c)

Jika a bukan bilangan terkecil, maka b atau c, salah satunya bisa merupakan bilangan

terkecil. Kita hanya perlu membandingkan apakah b lebih kecil dari c dengan

if (b < c)

Jika b lebih kecil dari c, berarti kita tahu bahwa b adalah bilangan terkecil. Tetapi kita

belum tahu apakah bilangan terkecil berikutnya adalah a atau c, sehingga kita harus

menguji lagi dengan

if (a < c)

Jika a lebih kecil dari c, maka urutannya adalah b, a, c. Jika tidak, maka urutannya adalah

b, c, a.

Demikian halnya apabila jika b > c, maka kita bisa tentukan urutan bilangannya.

Keseluruhan logika ini, bisa kita tuangkan dalam bentuk :

if (a < b && a < c) {

if (b < c)

System.out.println(a + " " + b + " " + c);

else

System.out.println(a + " " + c + " " + b);

} else if (b < c) {

if (a < c)

System.out.println(b + " " + a + " " + c);

else

System.out.println(b + " " + c + " " + a);

Page 79: Java With Eclipse

} else {

if (a < b)

System.out.println(c + " " + a + " " + c);

else

System.out.println(c + " " + b + " " + a);

}

Logika di atas bisa juga dituangkan dengan cara lain, yaitu melihat urutannya. Pertama kita

cek apakah a < b. Jika ya, kita tahu bahwa urutannya pasti a terlebih dahulu baru b.

Kemudian kita lihat apakah c berada di sebelah kiri a atau disebelah kanan b atau di

tengah-tengah.

Demikian seterusnya jika urutannya b terlebih dahulu baru a. Sehingga kodenya bisa

dituliskan dalam bentuk :

if (a < b) {

if (c < a)

System.out.println(c + " " + a + " " + b);

else if (c > b)

System.out.println(a + " " + b + " " + c);

else

System.out.println(a + " " + c + " " + b);

} else {

if (c < b)

System.out.println(c + " " + b + " " + a);

else if (c > a)

System.out.println(b + " " + a + " " + c);

else

System.out.println(b + " " + c + " " + a);

}

Pernyataan switch

Penyataan percabangan kedua yang dimiliki Java adalah switch. Pernyataan switch lebih

jarang digunakan, tetapi sering bermanfaat apabila kita ingin menuliskan percabangan

multi arah.

Pernyataan switch memiliki bentuk sebagai berikut

switch (ekspresi) {

case nilai1:

perintah1

break;

case nilai2:

perintah2

break;

Page 80: Java With Eclipse

case nilai3:

perintah3

break;

default:

perintah_lain

}

Di sini pernyataan switch akan mencari nilai ekspresi yang sesuai dengan nilai-nilai yang

didaftarkan pada pernyataan case. Jika salah satu nilai ditemui, maka program akan

melompat ke cabang case tersebut dan melakukan perintah yang terdapat di sana. Jika

tidak ditemui, maka program akan melompat ke perintah yang terdapat pada pernyataan

default.

Catatan ekspresi hanya bisa berbentuk nilai bilangan bulat (int, short, dan sejenisnya) atau

karakter, sehingga kita tidak bisa menggunakan switch untuk mengevaluasi ekspresi yang

berbentuk String.

Pernyataan break di atas sebetulnya tidak harus selalu ada. Tetapi, perintah break di sini

memerintahkan komputer agar segera keluar dari blok switch apabila perintah tersebut

telah selesai dilaksanakan.

Apabila perintah break tidak diberikan, maka program akan terus mengeksekusi perintah

lain meskipun sudah berada di luar nilai yang tertera dalam pernyataan casenya.

Misalnya, lihat kode berikut ini :

switch (N)

case 1:

System.out.println("Angka tersebut bernilai 1");

break;

case 2:

case 3:

case 4:

case 5:

System.out.println("Angka tersebut bernilai 2, 3, 4, atau 5");

break;

case 6:

case 7:

case 8:

System.out.println("Angka tersebut bernilai 6, 7, atau 8");

break;

default:

System.out.println("Angka tersebut tidak bernilai 1 - 8");

}

Page 81: Java With Eclipse

Salah satu aplikasi di mana pernyataan switch berguna adalah untuk memproses menu.

Menu memiliki beberapa pilihan dan user akan diminta untuk memilih suatu pilihan. Kita

dapat menggunakan switch untuk menginstruksikan komputer untuk melakukan tugas

tertentu sesuai dengan menu yang dipilih oleh user.

Jika Anda ingat pernyataan main() pada program Java, pernyataan main memiliki

parameter String[] args, di mana args merupakan argumen yang diberikan pada saat

program dijalankan melalui konsol. Biasanya argumen yang diberikan berupa opsi

bagaimana program harus dilaksanakan. Di sini pernyataan switch juga berguna untuk

memilih bagaimana program akan berjalan.

Jika Anda terbiasa atau pernah bergaul dengan Linux, maka tidak asing untuk menemukan

perintah pada Linux seperti "ls -l" atau "tar xfz blabla". Di sini ls atau tar adalah nama

program dan "-l" atau "xfz blabla" adalah argumen yang diberikan pada saat program

dijalankan.

Pernyataan kosong

Pernyataan kosong sebenarnya merupakan blok kosong, atau sama dengan {} tanpa

perintah apa-apa di dalamnya. Dalam Java, pernyataan kosong juga berarti tanda ; sehingga

apabila Anda menulis kode seperti

if (x > 0);

perintah1

maka perintah1 akan tetap dilaksanakan walaupun x <= 0.

Perintah kosong sering merupakan sumber kesalahan dan salah satu fitur yang sulit untuk

dicari kesalahannya pada saat debugging. Misalnya perintah berikut

for (i = 1; i <= 10; i++);

System.out.println("hallo");

Anda berharap untuk mencetak kata hallo 10x di layar, pada kenyataannya hanya 1 hallo

yang dicetak. Kenapa? Karena tanda ";" setelah for menyatakan bahwa program tidak

melakukan apa-apa di dalam perulangan. Perintah System.out.println("hallo") tidak

berada di dalam perulangan for, sehingga perintah ini hanya dijalankan 1x saja.

Pernyataan-pernyataan lain

Hingga saat ini kita sudah membahas hampir semua pernyataan yang Java sediakan. Ada

beberapa yang akan kita bahas kemudian, tetapi pernyataan-pernyataan ini merupakan

pernyataan lanjutan Java, seperti return yang digunakan untuk membuat subrutin sendiri,

atau try...catch dan throw untuk mengontrol alur kesalahan apabila ditemui di tengah

Page 82: Java With Eclipse

program (atau dengan kata lain eksepsi atau pengecualian), dan synchronized untuk

mengatur kontrol untuk multi-threading.

Beberapa kata kunci lain akan juga dibahas pada subjek tentang pemrograman berorientasi

objek yang akan kita bahas kemudian.

Contoh Program switch case

Kita akan membuat program untuk main suit. Permainan ini terdiri dari dua pemain. Dalam

permainan real, biasanya dua pemain mengadu suit dengan saling menunjukkan jari.

Hanya 3 jari yang boleh dipakai: jempol, telunjuk, dan kelingking.

Peraturannya : Jempol menang lawan Telunjuk, Telunjuk menang lawan Kelingking, dan

Kelingking menang lawan Jempol.

Dalam program ini, komputer adalah pemain pertama, sedangkan user adalah pemain

kedua. Komputer akan menggunakan perintah random() untuk menghitung salah satu dari

3 pilihan. 0 berarti Jempol, 1 berarti Telunjuk, dan 2 berarti Kelingking.

User akan diberikan pertanyaan untuk memasukkan J untuk Jempol, T untuk Telunjuk dan

K untuk Kelingking. Kemudian dengan pernyataan switch, J, T, dan K akan diubah

menjadi angka 0, 1, dan 2 seperti pada komputer.

Hasil perhitungan komputer dan hasil input dari user akan dibandingkan, kemudian

hasilnya siapa yang menang akan ditampilkan di layar.

package mainsuit;

import java.io.*;

public class MainSuit {

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

// cetak aturan main dan input dari user

System.out.println("Permainan suit");

System.out.println("==============");

System.out.println("Masukkan salah satu dari 3 kemungkinan :");

System.out.println("J untuk Jempol");

System.out.println("T untuk Telunjuk");

Page 83: Java With Eclipse

System.out.println("K untuk Kelingking");

System.out.println(""); // baris kosong

// variabel untuk mengambil input dari user

BufferedReader br = new BufferedReader(new

InputStreamReader(System.in));

String input = null;

// Kita gunakan pernyataan while hingga user betul

// memasukkan salah satu dari J, T, atau K

boolean inputvalid = false;

int suitKomputer = 0;

int suitUser = 0;

// selama input tidak valid, jalankan perulangan ini

while (!inputvalid) {

System.out.print("Masukkan suit Anda ");

try {

input = br.readLine();

} catch (IOException ioe) {

System.out.println("Kesalahan IO, program berhenti");

System.exit(1);

}

// pastikan bahwa user memasukkan sesuatu dengan mengecek

// apakah panjang input > 0

// jika tidak, maka perulangan akan dilanjutkan

if (input.length() > 0) {

switch (input.charAt(0)) {

case 'j' :

case 'J' :

suitUser = 0;

inputvalid = true; // kita ganti inputvalid agar

perulangan selesai

break;

case 't' :

case 'T' :

suitUser = 1;

inputvalid = true; // kita ganti inputvalid agar

perulangan selesai

Page 84: Java With Eclipse

break;

case 'k' :

case 'K' :

suitUser = 2;

inputvalid = true; // kita ganti inputvalid agar

perulangan selesai

break;

}

}

}

// sekarang menghitung suit komputer

// Math.random() menghasilkan nilai acak antara 0 dan 1

// jika kita kali 3, berarti menghasilkan nilai acak antara 0 dan

3

// perintah (int) untuk mengubah bilangan real menjadi bilangan

bulat

// dengan cara menghilangkan semua digit di belakang koma

// misalnya 1.0232323 menjadi 1

suitKomputer = (int)(Math.random()*3);

// suatu saat "mungkin" nilai random bernilai 3

// karena 3 tidak ada dalam suit kita, maka kita harus ubah

menjadi

// salah satu suit yang valid, yaitu 0, 1, atau 2

// dalam hal ini kita ganti jadi 2

if (suitKomputer == 3) {

suitKomputer = 2;

}

// menghitung siapa yang menang

switch (suitKomputer) {

// jika komputer Jempol

case 0:

// cetak suit Komputer

System.out.println("Suit komputer = Jempol");

switch (suitUser) {

// jika user Jempol

case 0:

System.out.println("Seri");

break;

Page 85: Java With Eclipse

// jika user Telunjuk

case 1:

System.out.println("Anda kalah");

break;

// jika user Kelingking

case 2:

System.out.println("Anda menang");

break;

}

break; // dari switch(suitKomputer)

// jika komputer Telunjuk

case 1:

// cetak suit Komputer

System.out.println("Suit komputer = Telunjuk");

switch (suitUser) {

// jika user Jempol

case 0:

System.out.println("Anda menang");

break;

// jika user Telunjuk

case 1:

System.out.println("Seri");

break;

// jika user Kelingking

case 2:

System.out.println("Anda kalah");

break;

}

break; // dari switch(suitKomputer)

// jika komputer Kelingking

case 2:

// cetak suit Komputer

System.out.println("Suit komputer = Kelingking");

switch (suitUser) {

// jika user Jempol

case 0:

System.out.println("Anda kalah");

break;

// jika user Telunjuk

case 1:

System.out.println("Anda menang");

Page 86: Java With Eclipse

break;

// jika user Kelingking

case 2:

System.out.println("Seri");

break;

}

break; // dari switch(suitKomputer)

}

}

}

Berikut ini adalah contoh keluaran program

Listing program di atas dapat Anda unduh melalui zip file atau pada gudang SVN di

alamat http://belajarjava.googlecode.com/svn/trunk/MainSuit

Bab V - Subrutin

Pemrograman kompleks

Salah satu cara yang digunakan untuk memecah permasalahan kompleks menjadi

permasalahan yang lebih kecil adalah dengan subrutin. Subrutin terdiri dari sekelompok

perintah yang melakukan suatu tugas tertentu. Subrutin memiliki nama, sehingga bisa

dipanggil suatu saat di bagian lain program. Ketika komputer mengeksekusi program,

setiap kali nama subrutin tersebut dipanggil, program akan melaksanakan semua perintah

di dalam subrutin tersebut.

Subrutin boleh digunakan berulang-ulang, dari mana pun di dalam program, bahkan dari

dalam subrutin lain. Dengan cara ini kita bisa membuat subrutin sederhana yang digunakan

berulang-ulang di seluruh program. Dengan cara ini, program besar dapat dipecah menjadi

bagian-bagian kecil. yang masing-masing melakukan perintah sederhana.

Seperti dijelaskan sebelumnya, subrutin bisa berbentuk statik atau non-statik. Dalam

bagian ini hanya subrutin statik yang akan dijelaskan lebih lanjut. Subrutin non-statik

Page 87: Java With Eclipse

adalah inti dari pemrograman berorientasi objek, dan akan dijelaskan lebih lanjut pada bab

berikutnya.

Kotak Hitam

Subrutin terdiri dari beberapa instruksi yang melakukan suatu tugas tertentu, dikumpulkan

dalam satu himpunan, dan diberi nama. Kumpulan instruksi ini bisa berupa suatu "tugas"

yang cukup kompleks yang disatukan sebagai konsep. Dengan cara ini, kita tidak perlu lagi

memikirkan bagaimana komputer melakukan tugas hingga sedetail mungkin. Kita hanya

perlu memanggil nama subrutin tersebut untuk menjalankan suatu "tugas".

Subrutin sering juga disebut dengan kotak hitam (atau black box) karena kita tidak perlu

(atau tidak mau tahu) secara detail apa yang subrutin tersebut lakukan. Kita hanya ingin

tahu hubungan kotak hitam tersebut dengan dunia luar. Hubungan ini disebut antar muka

(interface). Di kotak itu mungkin ada tombol yang bisa kita tekan, ada keyboard yang bisa

kita ketik, atau mungkin ada jendela untuk memberi dan mengambil informasi dari

dalamnya.

Karena kita bermaksud untuk menyembunyikan kompleksitas (dalam hal ini program yang

akan kita buat), ada beberapa prinsip penting dari suatu kotak hitam :

Antar muka harus sederhana, jelas, tak berbelit-belit, dan mudah dimengerti.

Apa contoh kotak hitam di sekitar kita? Sebetulnya banyak, misalnya TV, handphone,

DVD player, MP3 player, kulkas, AC, dan sebagainya. Kita bisa menyalakan TV,

mengganti channel, menaikkan dan menurunkan volume dengan mudah tanpa harus

mengetahui bagaimana TV bekerja. Sama halnya dengan AC, Anda cukup menekan

tombol, menaikkan dan menurunkan temperatur tanpa harus mengerti bagaimana AC

bekerja.

Sekarang mari kita lihat bagian dalam dari kotak hitam. Bagaimana AC atau TV bekerja

disebut dengan implementasi. Aturan kedua dari suatu kotak hitam adalah :

Untuk menggunakan kotak hitam, kita tidak perlu mengetahui tentang implementasi.

Yang kita butuh adalah antar muka.

Sebetulnya kita bisa mengganti implementasi selama apa yang dihasilkan dan apa yang

diminta tetap sama. Atau dengan kata lain, perilakunya tidak berubah. Misalnya dulu TV

menggunakan tabung hampa, tetapi sekarang menggunakan transistor. Tetapi perubahan

implementasi (dari tabung hampa ke transistor) tidak mengubah bagaimana TV

dioperasikan dan apa yang kita lihat di TV. Seperti halnya dengan program, kita bisa

mengubah implementasi suatu subrutin, misalnya mengoptimasi subrutin tersebut agar

Page 88: Java With Eclipse

lebih cepat, tanpa mengubah perilaku program secara keseluruhan, dan tanpa mengubah

perilaku fungsi lain yang memanggil subrutin tersebut.

Tentunya untuk membuat kotak hitam, kita perlu mengetahui detail tentang

implementasinya. Kotak hitam digunakan untuk membantu baik si pembuat kotak hitam

maupun penggunanya. Pada akhirnya, kotak hitam tersebut akan digunakan dalam

bermacam-macam situasi. Orang yang membuat kotak hitam ini tidak perlu mengerti

bagaimana kotaknya akan digunakan. Pembuatnya hanya menjamin bahwa kotak yang

dibuat harus bekerja sesuai dengan yang diharapkan. Aturan ketiga dari kotak hitam :

Pembuat (implementor) kotak hitam tidak perlu tahu bagaimana kotak hitam itu

akan digunakan.

Dengan kata lain, kotak hitam membagi dunia menjadi 2 bagian, yaitu bagian luar, untuk

apa ia digunakan, dan bagian dalam, yaitu detail bagaimana ia bekerja.

Antar muka kotak hitam tidak melulu harus berupa koneksi fisik antara kotak hitam

dengan dunia luar. Antar muka bisa juga berupa spesifikasi yang menjelaskan apa yang

dilakukan kotak hitam tersebut, dan perilakunya terhadap input yang diterimanya. Tidak

cukup untuk menyebutkan bahwa TV membutuhkan colokan kabel, tetapi harus disebutkan

bahwa colokan kabel digunakan untuk menyambung aliran listrik ke TV supaya TV bisa

dinyalakan.

Dalam bahasa pemrograman, antar muka dari suatu subrutin harus terdiri dari komponen

sintaks dan semantik. Bagian sintaks dari subrutin mengandung penjelasan tentang apa

yang harus diketik untuk memanggil subrutin tersebut. Dan bagian semantik menjelaskan

bagaimana secara khusus apa yang akan dikerjakan oleh subrutin tersebut. Untuk menulis

program yang benar, kita harus mengetahui spesifikasi sintaks dari subrutin tersebut.

Untuk mengerti dan bisa menggunakan subrutin tersebut seefektif mungkin, kita harus

mengetahui spesifikasi semantiknya. Kedua bagian dari subrutin tersebut bisa disebut

sebagai kontrak subrutin.

Kontrak subrutin bisa dijelaskan seperti "Ini yang harus Anda lakukan untuk menggunakan

saya, dan ini yang akan saya lakukan untuk Anda". Ketika kita menulis subrutin, komentar

yang kita tulis untuk menjelaskan subrutin itu harus memuat kontrak subrutin tersebut.

Kadang kala kontrak ini seringkali tidak dituliskan dengan benar, sehingga programmer

yang menggunakannya harus menerka apa yang akan dilakukan. Tentu saja ini tidak

efisien dan menghabiskan banyak waktu apabila kita harus menerka semua subrutin yang

terdapat dalam bahasa pemrograman.

Page 89: Java With Eclipse

Subrutin Statik dan Variabel Statik

Setiap subrutin yang dideklarasikan dalam Java harus dideklarasikan di dalam suatu Kelas

(Class). Hal ini mungkin membuat Java sedikit tidak normal, karena dalam bahasa

pemrograman lain, subrutin bisa diletakkan di mana saja termasuk di luar kelas. Salah satu

dari fungsi kelas adalah menggabungkan subrutin dan variabel bersama. Dan ini tidak

mudah apabila subrutin dan variabel berada di luar kelas, terutama apabila beberapa paket

harus digabungkan menjadi satu seperti dalam program kompleks. Akan terdapat banyak

kebingungan yang mungkin diakibatkan dari nama subrutin atau nama variabel yang sama.

Subrutin yang dideklarasikan di dalam kelas disebut dengan metode (method). Di bab

kemudian kita akan menggunakan istilah metode, tetapi bab ini kita akan menggunakan

subrutin dalam artiannya sebagai subrutin statik. Metode akan digunakan untuk subrutin

non-statik yang lebih merupakan sifat dari objek, dan bukan bagian dari kelas itu sendiri.

Definisi subrutin dalam bahasa pemrograman Java dapat dituliskan dalam bentuk

sifat tipe_keluaran nama_subrutin ( daftar parameter ) {

perintah

}

Kita sudah pernah mendefinisikan suatu subrutin, yaitu subrutin main(). Paling tidak kita

sudah kenal bagaimana subrutin didefinisikan.

perintah yang terdapat di antara { dan } disebut juga badan subrutin. Perintah ini

merupakan badan atau implementasi suatu subrutin, seperti yang dibahas sebelum pada

penjelasan tentang kotak hitam. Perintah ini merupakan instruksi yang akan dieksekusi

oleh komputer pada saat subrutin ini dipanggil.

sifat adalah sifat dari subrutin itu sendiri. Beberapa sifat yang pernah kita lihat adalah

static dan public. Ada lebih dari selusin sifat yang bisa diberikan kepada subrutin.

Jika kita akan membuat fungsi, yaitu subrutin yang menghitung suatu nilai kemudian

mengembalikan hasilnya, maka tipe_keluaran adalah tipe data dari keluaran yang

dihasilkan oleh fungsi tersebut. Kita akan membahas lebih lanjut tentang keluaran pada

bagian berikutnya. Jika subrutin kita bukan fungsi dan tidak menghasilkan nilai apa-apa,

kita gunakan tipe data spesial yang dinamakan void untuk menunjukkan bahwa tidak ada

nilai keluaran yang akan dikembalikan oleh subrutin tersebut.

Akhirnya kita sampai pada daftar parameter. Parameter adalah bagian dari antar muka

suatu subrutin. Parameter adalah informasi yang diberikan kepada suatu subrutin dari dunia

luar, untuk digunakan dalam eksekusi subrutin tersebut. Kasus sederhana misalnya televisi

memiliki subrutin gantiChannel(). Pertanyaan yang akan muncul adalah ganti channel ke

Page 90: Java With Eclipse

mana? Dalam hal ini parameter dapat digunakan, misalnya channel berbentuk bilangan

bulat (int) dan deklarasi subrutin gantiChannel dapat berbentuk seperti

public void gantiChannel(int channel) {

...

}

Pernyataan tersebut berarti subrutin gantiChannel() memiliki parameter channel yang

bertipe int. Akan tetapi channel belum memiliki nilai. Nilainya akan diberikan pada saat

subrutin ini dipanggil, misalnya dengan

gantiChannel(17);

Daftar parameter dari suatu subrutin bisa juga kosong, atau bisa berisi lebih dari satu

parameter dalam bentuk

tipe_data nama_parameter

Jika ada lebih dari satu parameter, maka parameter-perameter tersebut dihubungkan

dengan koma. Catatan bahwa masing-masing parameter harus terdiri dari satu tipe data dan

satu nama, misalnya double x, double y dan bukan double x, y.

Parameter akan dijelaskan lebih lanjut pada bagian berikutnya.

Berikut ini adalah beberapa contoh deklarasi subrutin yang umum dilakukan :

public static void mainGame() {

// "public" dan "static" ada sifat; "void" adalah tipe_keluaran

// "mainGame" adalah nama subrutin

// daftar parameternya kosong

... // perintah untuk memainkan game ditulis di bagian ini

}

int ambilNdata(int N) {

// tidak ada sifat, "int" adalah tipe_keluaran

// "ambilNdata" adalah nama subrutin

// dan parameternya adalah N yang memiliki tipe data int

... // perintah untuk mengambil N data ditulis di bagian ini

}

static boolean kurangDari(double x, double y) {

// "static" adalah sifat, "boolean" adalah tipe_keluaran

// "kurangDari" adalah nama subrutin

// parameternya ada 2, yaitu x yang bertipe data double

// dan y yang juga bertipe data double

.... // perintah untuk menguji apakah x lebih kecil dari y ditulis di

sini

Page 91: Java With Eclipse

}

Dalam contoh kedua perhatikan bahwa sifatnya tidak "static", dan subrutin ini tidak

dibahas dalam bagian ini. Subrutin pertama memiliki sifat "public" yang artinya subrutin

ini dapat dipanggil dari bagian manapun, termasuk dari luar kelas yang dimaksud.

Ada lagi sifat subrutin yaitu "private" yang berarti hanya bisa dipanggil dari dalam kelas di

mana subrutin tersebut didefinisikan. Sifat "public" dan "private" dinamakan penentu akses

(access specifier). Jika tidak disebutkan jenis aksesnya dalam suatu definisi subrutin, maka

otomatis subrutin itu memiliki sifat "package" yang artinya hanya bisa dipanggil dari paket

yang sama dari kelas di mana ia didefinisikan. Sifat lain yang berkaitan dengan penentu

akses adalah "protected" dan ini akan dibahas lebih lanjut pada bagian tentang

pemrograman berorientasi objek.

Ingatkah bahwa subrutin main() yang biasa kita gunakan dalam contoh-contoh program

memiliki bentuk seperti berikut ?

public static main(String[] args) { ... }

Sifatnya adalah "public" dan "static", namanya adalah "main" dan parameternya adalah

args yang bertipe data array dari String. Kita akan bahas tentang array pada bagian struktur

data di bab berikutnya.

Menjalankan subrutin

Ketika kita mendefinisikan subrutin, pada dasarnya kita memberitahu Java bahwa suatu

subrutin tersedia dalam suatu kelas. Subrutin tersebut tidak dijalankan sampai ia dipanggil.

(Hal ini juga berlaku untuk kelas main(), meskipun kita tinggal memanggilnya secara

spesifik, tetapi sistem operasi kita akan memanggil subrutin main() pada saat program

tersebut dijalankan.) Misalnya subrutin mainGame() di atas dapat dipanggil seperti

mainGame();

Pernyataan ini dapat dipanggil di mana pun dalam kelas yang sama, meskipun dari dalam

subrutin yang berbeda, misalnya dari dalam subrutin main(). Karena mainGame() bersifat

"public", maka subrutin ini dapat juga dipanggil dari luar kelas di mana ia dideklarasikan.

Misalnya, mainGame() dideklarasikan dalam kelas Gundu, maka mainGame dapat

dipanggil dari kelas lain seperti

Gundu.mainGame();

Penggunaan nama kelas di sini berarti memberitahu Java di mana subrutin mainGame()

harus dicari (dalam hal ini di dalam kelas Gundu). Dan ini juga membedakan bahwa yang

dimainkan adalah Gundu, bukan Catur.mainGame() atau UlarTangga.mainGame().

Lebih umum, untuk memanggil suatu subrutin dapat dilakukan dengan bentuk

Page 92: Java With Eclipse

nama_subrutin(parameter);

Dan untuk memanggil subrutin dari luar kelas, bisa dilakukan dalam bentuk

nama_kelas.nama_subrutin(parameter);

apabila subrutin tersebut memiliki sifat "static". Apabila subrutin tersebut tidak bersifat

"statik" maka nama_kelas harus diganti dengan nama objek jelmaan kelas tersebut. (Akan

dijelaskan lebih jauh nanti pada bab tentang pemrograman berorientasi objek).

Juga, apabila tidak ada parameter yang didefinisikan, maka parameter bisa dihilangkan,

tetapi tanda kurungnya () harus tetap ada meskipun kosong.

Variabel Statik

Suatu kelas juga bisa berisi lain hal selain subrutin, misalnya variabel. Variabel juga bisa

didefinisikan di dalam subrutin, yang disebut variabel lokal. Untuk membedakannya

variabel yang didefinisikan di dalam kelas disebut variabel anggota, karena variabel

tersebut adalah anggota kelas.

Seperti subrutin, variabel juga bisa bersifat statik atau non-statik. Di bagian ini kita hanya

akan membahas variabel statik. Variabel anggota statik dimiliki oleh kelas, dan akan selalu

ada selama kelas tersebut ada. Komputer akan menyiapkan memori tempat penyimpanan

variabel statik pada saat Java interpreter dijalankan.

Perubahan variabel statik ini akan mengubah isi memori tersebut, tidak peduli dari bagian

program mana perubahan variabel ini dilakukan. Kapanpun variabel ini dipanggil, ia akan

mengambil data dari memori di mana dia disimpan, tidak peduli bagian program mana

yang memanggilnya. Ini berarti variabel statik bisa diisi dari subrutin satu dan dimodifikasi

dari subrutin lainnya. Atau dengan kata lain, variabel statik digunakan bersama oleh

seluruh bagian kelas. Variabel lokal hanya bisa dipanggil dari dalam subrutin di mana ia

dideklarasikan.

Deklarasi variabel anggota dapat dilakukan dengan cara yang sama seperti deklarasi

variabel dalam subrutin. Kecuali, mereka juga bisa diberi attribut yang berisi sifatnya,

misalnya "static", "public", atau "private". Misalnya,

static int umur;

static private double x;

Seperti halnya penentu akses pada subrutin, variabel yang bersifat "private" hanya bisa

diakses dari dalam kelas yang bersangkutan, sedangkan variabel "public" dapat diakses

dari manapun. Misalnya, kelas System, memiliki variabel out, sehingga bisa kita panggil

dari luar dengan System.out.

Page 93: Java With Eclipse

Ketika kita mendeklarasikan suatu variabel di dalam subrutin, kita harus memberi nilai

awal untuk variabel tersebut. Akan tetapi untuk variabel anggota, Java otomatis memberi

nilai awal, sehingga tidak perlu diinisialisasi terlebih dahulu. Misalnya int akan diberi nilai

awal 0, karakter diberi nilai yang berupa karakter dengan nilai Unicode 0, dan String diberi

nilai awal null, sementara boolean diberi nilai awal false.

Kita tentu saja bisa memberi nilai awal apabila nilai awal yang diberi Java tidak cocok

dengan aplikasi kita. Caranya misalnya dengan memberi nilai di awal subrutin main().

Contoh Subrutin - Mengubah Program RataRata

Kita mulai dengan program RataRata yang kita bahasa pada bagian tentang while dan do ...

while.

Perhatikan bahwa di dalam program tersebut kita harus menulis berulang-ulang untuk

mendapatkan input data dari user. Sekarang kita akan pisahkan kode untuk mengambil

input dari user dalam kelas yang dinamakan KonsolInput.

Pertama-tama buat project baru di Eclipse yang dinamakan RataRata2. Caranya "File ->

New -> Java Project" kemudian masukkan RataRata2.

Page 94: Java With Eclipse

Kemudian buat kelas baru yang dinamakan KonsolInput. "File -> New -> Class",

masukkan package ratarata2 dan Name KonsolInput seperti gambar berikut :

Kita akan membuat subrutin statik yang dinamakan ambilDouble() yang tugasnya memberi

pertanyaan kepada user, mengambil input dari user dan mengubah input dari user menjadi

bilangan real (tipe data double).

Kita bisa definisikan subrutin ini dengan

public static double ambilDouble(String pertanyaan) { ... }

"public" dan "static" adalah sifat subrutin ini, "double" merupakan tipe data keluarannya

karena kita ingin subrutin ini mengembalikan input bertipe double sehingga siap untuk

digunakan. "ambilDouble" adalah nama subrutin ini, dan "String pertanyaan" adalah

parameternya yaitu berupa pertanyaan yang diberikan kepada user pada saat data akan

diambil.

Apa yang harus dilakukan sekarang? Kita ambil perintah untuk mengambil data dari

RataRata yaitu :

double bilangan = 0;

BufferedReader br = new BufferedReader(new

InputStreamReader(System.in));

Page 95: Java With Eclipse

String strbilangan = null;

System.out.print("Masukkan bilangan pertama : ");

try {

strbilangan = br.readLine();

} catch (IOException ioe) {

System.out.println("Kesalahan IO, program berhenti");

System.exit(1);

}

// mengubah input menjadi double agar bisa diproses lebih lanjut

bilangan = Double.parseDouble(strbilangan);

Ingat bahwa parameter "String pertanyaan" adalah pertanyaan yang akan kita ajukan

kepada user, sehingga baris

System.out.print("Masukkan bilangan pertama : ");

kita ganti dengan

System.out.print(pertanyaan);

Kita akan kembalikan "bilangan" kepada si pemanggil, sehingga di akhir subrutin kita beri

perintah

return bilangan;

yang berarti kembalikan bilangan kepada si pemanggil fungsi ini.

Jangan lupa juga untuk menambahkan import java.io.*; di awal kelas karena

BufferedReader adalah anggota dari paket ini. Sekarang subrutin ambilDouble() menjadi

seperti ini

package ratarata2;

import java.io.*;

public class KonsolInput {

public static double ambilDouble(String pertanyaan) {

String strbilangan = null;

BufferedReader br = new BufferedReader(new

InputStreamReader(System.in));

double bilangan;

System.out.print(pertanyaan);

try {

strbilangan = br.readLine();

Page 96: Java With Eclipse

} catch (IOException ioe) {

System.out.println("Kesalahan IO, program berhenti");

System.exit(1);

}

bilangan = Double.parseDouble(strbilangan);

return bilangan;

}

}

Kemudian kita buat kelas baru yang dinamakan RataRata2, seperti pada gambar berikut :

Dengan menggunakan subrutin yang baru kita buat, kita modifikasi program RataRata

menjadi RataRata2 sebagai berikut :

package ratarata2;

public class RataRata2 {

/**

* @param args

*/

public static void main(String[] args) {

// TODO Auto-generated method stub

Page 97: Java With Eclipse

double jumlah = 0;

double bilangan = 0;

int n = 0;

bilangan = KonsolInput.ambilDouble("Masukkan bilangan pertama :

");

while (bilangan != 0) {

jumlah += bilangan; // sama dengan : jumlah = jumlah +

bilangan

n++; // sama dengan : n = n+1

bilangan = KonsolInput.ambilDouble("Masukkan bilangan

berikutnya (atau 0 untuk mengakhiri) : ");

}

// hitung rata-rata

double ratarata = jumlah/n;

// cetak hasilnya ke layar

if (n == 0) {

System.out.println("Data kosong, rata-rata tidak bisa

dihitung");

} else {

System.out.println("Anda memasukkan " + n + " data");

System.out.println("Rata-rata bilangan yang dimasukkan adalah

" + ratarata);

}

}

}

Perhatikan beberapa perubahan di dalam program ini. Pertama, kita hapus perintah-

perintah untuk mengambil input dari user karena sudah kita implementasikan pada subrutin

ambilDouble() di kelas KonsolInput.

Di kelas RataRata2, kita panggil subrutin ambilDouble() dengan perintah

bilangan = KonsolInput.ambilDouble("Masukkan bilangan pertama :

");

dan

bilangan = KonsolInput.ambilDouble("Masukkan bilangan berikutnya

(atau 0 untuk mengakhiri) : ");

Page 98: Java With Eclipse

di mana parameternya adalah pertanyaan yang diberikan kepada user sebelum

memasukkan data.

Hasil keluarannya sama dengan pada kelas RataRata

Program di atas dapat diunduh dalam bentuk zip file atau melalui gudang SVN di alamat :

http://belajarjava.googlecode.com/svn/trunk/RataRata2

Contoh Program Subrutin dan Variabel Statik

Mari kita lihat kembali dan kita akan ubah program MainSuit pada halaman ini.

Pertama-tama kita akan ubah algoritma untuk menentukan siapa yang menang. Dalam

program sebelumnya, kita gunakan pernyataan switch case, yaitu kita tuliskan semua

kemungkinan dalam permainan.

Jika Anda ingat, Jempol dilambangkan dengan 0, Telunjuk dilambangkan dengan 1, dan

Kelingking dilambangkan dengan 2. Cara lain untuk menentukan pemenangnya adalah

dengan mengurangi suitKomputer dan suitUser

delta = suitKomputer - suitUser;

Apabila delta = 0, maka permainan berakhir seri. Sekarang coba masukkan apabila

komputer 0 (Jempol) dan user 1 (Telunjuk). Dalam hal ini delta bernilai -1, dan ini berarti

komputer menang. Dengan kata lain, apabila delta bernilai -1 maka komputer menang, dan

apabila delta bernilai 1, maka user menang.

Coba lagi juga komputer 0 (Jempol) dan user 2 (Kelingking). Dalam hal ini delta -2, dan

ini berarti user menang sesuai dengan aturan bahwa Kelingking menang melawan Jempol.

Dengan sifat seperti di atas, kita bisa ubah pernyataan switch case pada program

sebelumnya dengan

// menghitung siapa yang menang

int delta = suitKomputer - suitUser;

switch (delta) {

case 0:

seri++;

Page 99: Java With Eclipse

System.out.println("Hasilnya : Seri");

break;

case 1:

case -2:

userMenang++;

System.out.println("Hasilnya : Anda menang");

break;

case -1:

case 2:

komputerMenang++;

System.out.println("Hasilnya : Anda kalah");

break;

}

Kemudian, kita tambahkan lagi variabel statik yang fungsinya digunakan untuk mendata

berapa kali komputer menang, berapa kali Anda menang dan berapa kali seri.

public static int userMenang = 0;

public static int komputerMenang = 0;

public static int seri = 0;

Kemudian kita akan pindahkan sebagian kode pada subrutin main sehingga dapat dipanggil

berkali-kali tanpa harus menulis seluruh kode lagi. Kita namakan subrutin ini mainGame().

Kodenya sebagai berikut :

public static int userMenang = 0;

public static int komputerMenang = 0;

public static int seri = 0;

public static void mainGame() {

BufferedReader br = new BufferedReader(new

InputStreamReader(System.in));

// variabel untuk mengambil input dari user

String input = null;

// Kita gunakan pernyataan while hingga user betul

// memasukkan salah satu dari J, T, atau K

boolean inputvalid = false;

int suitKomputer = 0;

int suitUser = 0;

// selama input tidak valid, jalankan perulangan ini

while (!inputvalid) {

Page 100: Java With Eclipse

System.out.print("Masukkan suit Anda ");

try {

input = br.readLine();

} catch (IOException ioe) {

System.out.println("Kesalahan IO, program berhenti");

System.exit(1);

}

// pastikan bahwa user memasukkan sesuatu dengan mengecek

// apakah panjang input > 0

// jika tidak, maka perulangan akan dilanjutkan

if (input.length() > 0) {

switch (input.charAt(0)) {

case 'j' :

case 'J' :

suitUser = 0;

inputvalid = true; // kita ganti inputvalid agar

perulangan selesai

break;

case 't' :

case 'T' :

suitUser = 1;

inputvalid = true; // kita ganti inputvalid agar

perulangan selesai

break;

case 'k' :

case 'K' :

suitUser = 2;

inputvalid = true; // kita ganti inputvalid agar

perulangan selesai

break;

}

}

}

// sekarang menghitung suit komputer

// Math.random() menghasilkan nilai acak antara 0 dan 1

// jika kita kali 3, berarti menghasilkan nilai acak antara 0 dan

3

// perintah (int) untuk mengubah bilangan real menjadi bilangan

bulat

Page 101: Java With Eclipse

// dengan cara menghilangkan semua digit di belakang koma

// misalnya 1.0232323 menjadi 1

suitKomputer = (int)(Math.random()*3);

// suatu saat "mungkin" nilai random bernilai 3

// karena 3 tidak ada dalam suit kita, maka kita harus ubah

menjadi

// salah satu suit yang valid, yaitu 0, 1, atau 2

// dalam hal ini kita ganti jadi 2

if (suitKomputer == 3) {

suitKomputer = 2;

}

// menghitung siapa yang menang

int delta = suitKomputer - suitUser;

switch (delta) {

case 0:

seri++;

System.out.println("Hasilnya : Seri");

break;

case 1:

case -2:

userMenang++;

System.out.println("Hasilnya : Anda menang");

break;

case -1:

case 2:

komputerMenang++;

System.out.println("Hasilnya : Anda kalah");

break;

}

}

Sekarang kita ubah subrutin main()-nya, sehingga komputer akan menanyakan Anda untuk

main lagi atau tidak. Jika jawabannya ya, maka permainan akan diteruskan, dan statistik

menang-kalah-seri akan ditampilkan. Jika tidak, hanya tampilkan statistiknya saja,

kemudian program akan keluar.

public static void main(String[] args) {

// TODO Auto-generated method stub

Page 102: Java With Eclipse

// cetak aturan main dan input dari user

System.out.println("Permainan suit");

System.out.println("==============");

System.out.println("Masukkan salah satu dari 3 kemungkinan :");

System.out.println("J untuk Jempol");

System.out.println("T untuk Telunjuk");

System.out.println("K untuk Kelingking");

System.out.println(""); // baris kosong

while (true) {

mainGame();

// tampilkan statistik

System.out.println("Statistik :");

System.out.println("Komputer = " + komputerMenang + " Anda

= " + userMenang + " Seri = " + seri);

System.out.println(""); // baris kosong

// variabel untuk mengambil input dari user

BufferedReader br = new BufferedReader(new

InputStreamReader(System.in));

String input = null;

System.out.print("Main lagi (Y/T) ? ");

try {

input = br.readLine();

} catch (IOException ioe) {

System.out.println("Kesalahan IO, program berhenti");

System.exit(1);

}

// keluar jika jawabannya tidak

if ((input.charAt(0) == 't') || (input.charAt(0) == 'T'))

break;

}

}

Berikut ini hasil keluarannya

Page 103: Java With Eclipse

Dan program tersebut dapat Anda unduh dalam bentuk zip file atau melalui gudang SVN

di alamat : http://belajarjava.googlecode.com/svn/trunk/MainSuitSubrutin>

Parameter

Jika subrutin adalah kotak hitam, maka parameter merupakan alat atau mekanisme untuk

memberikan informasi dari dunia luar ke dalam kotak. Parameter merupakan bagian dari

antar muka dengan suatu subrutin. Dengan menggunakan parameter, kita dapat mengatur

perilaku suatu subrutin sesuai dengan input yang diberikan.

Sebagai analogi, kita ambil contoh AC -- yaitu alat yang mengatur suhu ruangan pada

temperatur konstan. AC memiliki parameter, yaitu tombol remote yang digunakan untuk

memasukkan suhu yang diinginkan. AC akan selalu melakukan tugas yang sama, yaitu

mengatur suhu ruangan. Akan tetapi, tugas persis yang dilakukan tergantung suhu yang

diterima dari remote control.

Kita ambil contoh pada kelas KonsolInput pada bagian terdahulu.

public static double ambilDouble(String pertanyaan) {

String strbilangan = null;

Page 104: Java With Eclipse

BufferedReader br = new BufferedReader(new

InputStreamReader(System.in));

double bilangan;

System.out.print(pertanyaan);

try {

strbilangan = br.readLine();

} catch (IOException ioe) {

System.out.println("Kesalahan IO, program berhenti");

System.exit(1);

}

bilangan = Double.parseDouble(strbilangan);

return bilangan;

}

Di sini subrutin ambilDouble memiliki parameter tunggal yang bertipe String dan bernama

pertanyaan. Ketika subrutin ini dipanggil, suatu nilai harus ikut disertakan, dan nilai ini

dimasukkan dalam parameter "pertanyaan". Misalnya, subrutin dapat dipanggil dengan a =

ambilDouble("hallo");. Ketika komputer menjalankan perintah ini, komputer akan

memasukkan "hallo" ke dalam parameter "pertanyaan", kemudian subrutin ambilDouble

menjalankan semua perintah di dalamnya.

Jika parameter yang diberikan berupa variabel, misalnya variabel str berisi "kabar",

kemudian subrutin ambilDouble panggil dengan perintah ambilDouble(str), maka isi

dari str akan dikopi ke dalam parameter "pertanyaan" terlebih dahulu sebelum seluruh

perintah dalam subrutin ambilDouble dilaksanakan.

Perlu dicatat bahwa istilah "parameter" digunakan dalam dua konsep berbeda tetapi

berkaitan. Yang pertama adalah "parameter" digunakan untuk mendefinisikan suatu

subrutin. Parameter yang digunakan pada definisi suatu subrutin disebut parameter

formal. "Parameter" yang digunakan untuk memanggil suatu subrutin disebut parameter

aktual. Ketika suatu subrutin dipanggil, parameter aktual akan dievaluasi, dan hasilnya

akan dimasukkan ke dalam parameter formal.

Parameter formal berbentuk seperti pengenal atau nama, dan bersifat seperti halnya

variabel dan memiliki tipe -- misalnya int, double, atau String. Parameter aktual bersifat

seperti nilai atau angka, jadi bisa berbentuk ekspresi atau perintah apapun yang

menghasilkan nilai atau angka. Sewaktu kita memanggil subrutin, kita harus memberi

parameter aktual untuk setiap parameter formal yang didefinisikan.

Page 105: Java With Eclipse

Misalnya, lihat contoh berikut.

static void lakukanSesuatu(int N, double d, boolean b) {

... // perintah lainnya di sini

}

Subrutin ini bisa saja dipanggil dengan instruksi berikut.

lakukanSesuatu(1, Math.random() * 3, x == 3);

Ketika komputer mengevaluasi subrutin ini, pada dasarnya sama dengan menglakukan

perintah-perintah berikut.

{

int N;

double d;

boolean b;

N = 1;

d = Math.random() * 3;

b = (x == 3);

... // perintah lainnya di sini

}

Untuk memanggil subrutin, yang kita perlukan adalah nama, berapa banyak parameter

formal yang dimilikinya, dan tipe dari parameter formal tersebut. Infomasi ini disebut juga

tanda subrutin. Tanda subrutin lakukanSesuatu dapat ditulis sebagai

lakukanSesuatu(int,double,boolean). Perhatikan bahwa tanda subrutin tidak memiliki nama

parameter; sebetulnya jika kita hanya ingin menggunakan subrutin tersebut, nama

parameter tidaklah penting, karena nama bukan merupakan bagian dari antar muka

subrutin.

Java agak sedikit berbeda karena memungkinkan dua atau lebih subrutin yang berbeda

dalam kelas yang sama memiliki tanda subrutin yang berbeda. (Mirip seperti bahasa C++)

Subrutin yang seperti ini disebut subrutin yang dibebanlebihkan (overloaded). Misalnya,

void taruh(String s) { ... }

void taruh(int i) { ... }

void taruh(double d) { ... }

void taruh(boolean b) { ... }

Kita sudah menggunakan subrutin yang dibebanlebihkan ini misalnya pada

System.out.println(). Jika kita coba ketik System.out.println pada Eclipse, Eclipse akan

membantu kita untuk melihat parameter apa saja yang dibutuhkan, termasuk juga dapat

menunjukkan apakah suatu subrutin dibebanlebihkan.

Page 106: Java With Eclipse

Catatan : Overload tidak berlaku pada tipe keluaran subrutin. Dua atau lebih subrutin

dengan nama sama dalam suatu kelas tetapi memiliki tipe keluaran yang berbeda tidak

diperbolehkan dalam Java. Misalnya, kalau kita coba definisikan 2 fungsi seperti berikut,

maka Java akan memberi pernyataan kesalahan sintaks.

int ambil(String s) { ... }

boolean ambil(String s) { ... }

double ambil(String s) { ... }

Terakhir, hingga saat ini kita sudah mengenal 3 jenis variabel: variabel lokal, yang

didefinisikan di dalam suatu blok, variabel statik, yang didefinisikan di dalam suatu kelas,

dan parameter formal, yang didefinisikan pada definisi subrutin di dalam suatu kelas.

Variabel lokal tidak memiliki hubungan dengan dunia luar sama sekali. Parameter

digunakan untuk berhubungan dengan dunia luar sebagai alat untuk menerima nilai atau

angka dari dunia luar. Perubahan nilai pada variabel lokal dan parameter tidak

mempengaruhi variabel itu sendiri karena sifatnya terisolasi.

Hal yang sama sekali berbeda akan terjadi apabila suatu subrutin menggunakan variabel

yang didefinisikan di luar subrutin itu. Variabel tersebut hidup secara terpisah dari

subrutin, dan bisa diakses oleh apapun di luar subrutin itu. Variabel ini disebut variabel

global. Variabel ini terlihat oleh semua subrutin dalam kelas tersebut. Perubahan yang

terjadi di sini dapat mempengaruhi cara kerja subrutin lain yang menggunakan variabel

tersebut.

Hal ini kita lihat pada contoh sebelumnya tentang subrutin dan variabel statik untuk

menghitung statistik komputerMenang, userMenang, dan seri.

Tidak ada yang salah dalam menggunakan variabel global, akan tetapi kita harus terus

ingat bahwa variabel global itu harus sebagai bagian dari antar muka subrutin kita dengan

Page 107: Java With Eclipse

keseluruhan dunia luar. Cara ini sebetulnya jalan belakang yang tidak terdokumentasi

seperti pada definisi subrutin, yang apabila aturan-aturannya tidak dipenuhi secara disiplin

mungkin akan menyebabkan bug atau kesalahan lain di luar kontrol kita.

Paling tidak ada satu alasan untuk menggunakan variabel global, yaitu apabila kelas secara

keseluruhan dianggap sebagai kotak hitam, akan lebih masuk akal apabila subrutin bisa

mengintip sedikit keluar dan saling menukar informasi jika cara ini jauh lebih mudah

diimplementasikan dan dilihat dari dunia luar.

Tipe Keluaran

Suatu subrutin yang mengembalikan nilai disebut fungsi. Suatu fungsi hanya dapat

mengembalikan nilai dengan tipe tertentu, yang disebut tipe keluaran. Fungsi biasanya

dipanggil di tempat suatu nilai atau angka ditempatkan, misalnya disebelah kanan tanda =,

pada parameter sewaktu subrutin dipanggil, atau di tengah-tengah ekspresi yang panjang.

Fungsi dengan tipe keluaran boolean juga bisa ditempatkan sebagai kondisi pada

pernyataan if atau while.

Sebetulnya boleh-boleh saja memanggil suatu fungsi sendirian tanpa diletakkan di sebelah

kanan tanda =, misalnya ambilDouble("hallo");. Dalam hal ini keluarannya akan

diabaikan oleh komputer. Tergantung program yang akan kita buat, kadang-kadang

program kita memang sengaja membuang nilai yang dikembalikan oleh fungsi karena satu

dan lain hal.

Untuk membuat fungsi yang mengeluarkan suatu nilai kembali kepada pemanggilnya, kita

harus memberikan pernyataan return, dengan bentuk

return ekspresi;

Pernyataan return ini hanya boleh ditulis di dalam sebuah subrutin (termasuk subrutin

main()), dan ekspresi harus menghasilkan nilai yang memiliki tipe yang sama dengan tipe

keluaran yang didefinisikan pada deklarasi subrutin tersebut.

Ketika komputer menjalankan pernyataan return, komputer akan menghitung "ekspresi",

menghentikan eksekusi subrutin tersebut saat itu juga, dan kembali pada si pemanggil

dengan membawa nilai hasil perhitungan "ekspresi".

Misalnya, kita akan menghitung luas suatu lingkaran. Kita buat fungsi luasLingkaran

dengan parameter jarijari dan keluarannya bertipe double, sebagai berikut :

static double luasLingkaran(double jarijari) {

return 3.14 * jarijari * jarijari;

}

Page 108: Java With Eclipse

Anggap komputer sedang melakukan perintah "ruangkosong = 20*20 -

luasLingkaran(10);". Ketika sampai pada bagian luasLingkaran, fungsi tersebut akan

memasukkan 10 ke dalam parameter formal jarijari. Di dalam badan fungsi, ia akan

menghitung 3.14 * 10.0 * 10.0 yang hasilnya 314.0. Nilai ini akan dikembalikan

kepada fungsi awalnya untuk menghitung "ruangkosong = 20*20 -

luasLingkaran(10);", sehingga fungsi ini juga bisa diganti dengan "ruangkosong =

400 - 314.0;" Hasilnya yaitu 86 dimasukkan ke dalam variabel ruangkosong

Dalam subrutin biasa -- yaitu subrutin yang tipe keluarannya void -- kita juga bisa

menggunakan perintah return untuk langsung keluar dari subrutin tersebut, yaitu dengan

perintah return; tanpa ekspresi. Di subrutin ini perintah return boleh diberikan jika

diperlukan, akan tetapi pada fungsi perintah return wajib diberikan.

Berikut ini adalah fungsi untuk menentukan nilai akhir (A, B, C, D atau E) dari nilai ujian.

static char nilaiAlfa(double nilaiUjian) {

if (nilaiUjian >= 90)

return 'A';

else if (nilaiUjian >= 80)

return 'B';

else if (nilaiUjian >= 70)

return 'C';

else if (nilaiUjian >= 60)

return 'D';

else

return 'E';

}

Perhatikan bahwa pada setiap cabang, perintah return diberikan. Ada juga beberapa

programmer yang lebih suka dengan menggunakan variabel sementara kemudian perintah

return diberi satu kali saja di akhir fungsi, seperti contoh berikut.

static char nilaiAlfa(double nilaiUjian) {

char keluaran;

if (nilaiUjian >= 90)

keluaran = 'A';

else if (nilaiUjian >= 80)

keluaran = 'B';

else if (nilaiUjian >= 70)

keluaran = 'C';

else if (nilaiUjian >= 60)

keluaran = 'D';

else

Page 109: Java With Eclipse

keluaran = 'E';

return keluaran;

}

Contoh di atas memiliki tipe keluaran char. Dan sebenarnya, tipe keluaran bisa bertipe apa

saja, termasuk kelas, seperti String pada contoh berikut.

static String ulangKarakter(char c, int n) {

String keluaran = "";

// tambah karakter c ke dalam String keluaran sebanyak n kali

for (int i = 1; i <= n; i++)

keluaran = keluaran + c;

return keluaran;

}

Fungsi di atas berfungsi untuk mengulang karakter c sebanyak n kali dan mengembalikan

hasilnya. Misalnya jika fungsi di atas kita panggil dengan "ulang =

ulangKarakter('A', 10);" maka ulang akan bernilai "AAAAAAAAAA" setelah fungsi

ulangKarakter dijalankan.

Toolbox, API, dan Paket

Dengan semakin mudah digunakannya komputer, akan semakin sulit dan kompleks tugas

yang harus dilakukan oleh programmer. Kita dengan mudah menulis program berbasis

konsol dengan menggunakan beberapa subrutin untuk menampilkan pesan dan mengambil

input dari user. GUI (graphical user interface = antarmuka pengguna berbasis grafik),

seperti jendela, tombol, scroll bar, menu, kotak input teks, dan lain-lain mungkin lebih

menarik bagi user. Akan tetapi programmer harus berhadapan dengan kemungkinan yang

sangat amat banyak, dan lebih banyak subrutin yang harus dibuat untuk bisa mengontrol

setiap komponen GUI.

Seseorang yang ingin membuat program untuk Windows misalnya, harus menggunakan

Toolbox Windows, yaitu koleksi dari ribuan subrutin, misalnya untuk membuka dan

menutup jendela, untuk menggambar dan menulis teks, menambah tombol dan menerima

respon dari user. Selain GUI ada juga subrutin yang berurusan dengan membuka,

membaca, dan menulis file, berkomunikasi dengan network, dan lain-lain. Intinya di dalam

toolbox terdapat segala sesuatu yang menurut kita standar. Apple dan Linux memiliki

toolbox sendiri yang berbeda dengan yang disediakan oleh Windows.

Page 110: Java With Eclipse

Setiap proyek pembuatan program akan terdiri dari inovasi (sesuatu yang sangat baru) dan

menggunakan kembali sesuatu yang sudah pernah dibuat sebelumnya. Seorang

programmer diberi peralatan untuk berhasil dalam proyeknya tersebut, yang paling dasar

adalah apa yang diberikan oleh bahasa pemrograman itu sendiri, misalnya variabel,

perulangan, dan lain-lain. Di atasnya, programmer bisa menambahkan toolbox yang berisi

subrutin yang sudah ditulis untuk melakukan tugas tertentu. Alat ini, jika didesain dengan

sangat baik, bisa dijadikan kotak hitam yang sesungguhnya, yaitu kotak hitam yang kita

betul-betul tidak perlu tahu bagaimana dia bekerja.

Bagian yang inovatif adalah bagaimana merangkai alat tadi menjadi sesuatu yang berguna

untuk suatu proyek atau suatu masalah yang ingin dipecahkan (pengolah kata, browser,

game, dll). Ini disebut pemrograman aplikasi.

Toolbox suatu software adalah juga kotak hitam. Ia memiliki antar muka dengan

programmernya. Antar muka ini berisi spesifikasi tentang rutin-rutin apa saja yang ada di

sana, apa parameter yang dibutuhkan, dan tugas apa yang mereka lakukan. Informasi ini

disebut API (Applications Programming Interface = Antarmuka Pemrograman untuk

Aplikasi). Windows API adalah spesifikasi semua subrutin yang tersedia dalam toolbox

Apple.

Perusahaan yang membuat hardware, misalnya sound card atau blue tooth mungkin juga

akan membuat API untuk perangkat tersebut yang berisi subrutin yang diperlukan

programmer untuk bisa menggunakan perangkat itu. Peneliti yang membuat program untuk

menyelesaikan suatu perhitungan matematis kompleks -- misalnya persamaan diferensial --

juga akan membuat API sehingga orang lain bisa menggunakan subrutinnya tanpa harus

mengerti dengan detail bagaimana perhitungan itu dilakukan.

Bahasa pemrograman Java juga didukung oleh API yang cukup besar dan standar. Contoh

API yang pernah kita lihat misalnya Math.random(), tipe data String, dan subrutin yang

terkait. Java API standar terdiri dari subrutin untuk membuat dan mengendalikan GUI,

untuk komunikasi network, baca tulis file, dan lain-lain. Mungkin lebih mudah jika API ini

dianggap sebagai bagian dari bahasa Java, akan tetapi sebetulnya API terdiri dari subrutin

yang bisa digunakan dalam program Java.

Java merupakan bahasa yang tidak tergantung pada mesin atau sistem operasi di mana ia

dijalankan. Java API yang sama harus bisa bekerja dengan semua sistem. Akan tetapi ingat

bahwa yang sama adalah antar mukanya. Implementasinya bisa sangat berbeda antara satu

sistem operasi dengan lainnya.

Page 111: Java With Eclipse

Sistem Java pada suatu komputer terdiri dari implementasi pada komputer tersebut.

Program Java yang kita buat memanggil subrutin pada komputer tersebut. Ketika komputer

menjalankan program dan sampai pada perintah untuk memanggil subrutin pada API,

komputer akan menjalankan implementasi subrutin yang sesuai dengan sistem komputer di

mana program tersebut dijalankan. Artinya, kita hanya perlu belajar satu API saja untuk

melakukan pemrograman pada berbagai macam komputer dan sistem operasi.

Seperti subrutin pada Java, subrutin pada API standar dikelompokkan ke dalam kelas.

Untuk membuat organisasi lebih baik, kelas Java dapat digabungkan menjadi paket. Kita

juga bisa membuat hirarki paket yang lebih tinggi, karena paket juga bisa berisi paket lain.

Kenyataannya seluruh API standar Java diimplementasikan dalam beberapa paket. Salah

satu paketnya bernama "java", yang berisi paket-paket non-GUI dan juga GUI berbasis

AWT. Paket lain, yaitu "javax", yang ditambahkan pada versi 1.2, berisi kelas yang

digunakan untuk GUI Swing.

Suatu paket dapat terdiri dari kelas ataupun pake lain. Paket yang terdapat di dalam paket

lain disebut "sub paket". Paket java dan javax juga memiliki beberapa sub paket. Salah

satu sub paket java, misalnya disebut awt. Karena awt berada di dalam paket java, nama

lengkapnya adalah java.awt. Paket inilah yang sebenarnya memiliki kelas-kelas GUI

AWT, seperti kelas Button yang melambangkan tombol, atau Graphics yang merupakan

kumpulan rutin untuk menggambar di monitor. Karena kelas ini tergabung dalam paket

java.awt, maka nama lengkapnya adalah java.awt.Button dan java.awt.Graphics.

Demikian juga dengan javax yang memiliki sub paket yang bernama javax.swing, yang

terdiri dari kelas yang bernama javax.swing.JButton dan javax.swing.JApplet.

Paket java sendiri terdiri dari beberapa sub paket lain, seperti java.io yang sudah kita

pakai dalam bab-bab sebelumnya, berguna untuk memberikan fasilitas input/output,

java.net untuk mengendalikan komunikasi jaringan, dan java.applet

mengimplementasikan fungsi dasar untuk applet. Paket yang paling dasar disebut

java.lang. Di dalam paket ini terdapat kelas-kelas fundamental misalnya String dan

Math.

Mungkin akan lebih jelas jika kita lihat ilustrasi berikut tentang tingkatan paket java, sub

paket, kelas di dalam sub paket, dan subrutin di dalam kelas.

Page 112: Java With Eclipse

Misalkan kita akan menggunakan kelas java.awt.Color dalam program. Salah satu

caranya adalah dengan menggunakan nama lengkap dari kelas tersebut, misalnya

java.awt.Color warnaKotak;

untuk mendekalarasi variabel bernama warnaKotak yang tipenya adalah java.awt.Color.

Tentu saja akan terlalu cape jika kita harus menulis nama kelas dengan lengkap setiap kali

akan digunakan. Java mengijinkan kita untuk menggunakan langsung nama kelasnya jika

kita berikan perintah import, seperti

import java.awt.Color;

di awal program, dan kemudian di bagian lain, kita bisa menyingkat java.awt.Color

hanya dengan nama kelasnya saja, sehingga menjadi

Color warnaKotak;

(Satu-satunya keuntungan yang diberikan perintah import adalah untuk menyingkat

penulisan nama kelas, bukan untuk mengimport keseluruhan kelas ke dalam program kita.

Seandainya perintah import tidak kita berikan, dan kita panggil dengan nama lengkapnya,

Java akan tetap bisa mengakses kelas tersebut.)

Ada lagi jalan pendek untuk mengimport seluruh kelas pada suatu paket. Kita bisa import

seluruh kelas di dalam paket java.awt dengan

import java.awt.*;

Ingat pada kode kita sebelumnya yang berbentuk seperti ini?

import java.io.*;

Ketika program yang akan kita buat semakin besar, maka ada kemungkinan kita harus

mengimport banyak paket. Satu hal penting yang perlu diingat adalah ada kemungkinan

beberapa paket memiliki nama kelas yang sama. Misalnya java.awt dan java.util

Page 113: Java With Eclipse

memiliki kelas yang bernama List. Jika kita import kedua kelas dengan perintah

java.awt.* dan java.util.*, maka nama List menjadi rancu.

Jika kita mencoba membuat variabel dengan tipe data List, java akan menampilkan pesan

kesalahan tentang adanya nama kelas yang membingungkan. Solusinya? Gunakan nama

lengkap kelasnya, yaitu java.awt.List atau java.util.List.

Karena paket java.lang.* adalah paket yang sangat penting, setiap kelas dalam

java.lang akan diimport otomatis ke dalam semua program. Seakan-akan semua program

dimulai dengan perintah "import java.lang.*;". Artinya, kita bisa memanggil kelas

seperti String saja, bukan java.lang.String, atau Math.sqrt() bukan

java.lang.Math.sqrt().

Programmer juga bisa membuat paket baru. Misalnya kita akan membuat kelas yang akan

diletakkan di paket yang bernama alatbantu. Maka pada awal program kita harus

didefinisikan dengan perintah "package alatbantu;" Program lain yang menggunakan

paket ini bisa menambah "import alatbantu.*;" untuk mengabmbil semua kelas dalam

paket alatbantu.

Masalahnya hal ini agak sedikit lebih rumit. Ingat bahwa ketika suatu program

menggunakan kelas, maka kelas tersebut harus "tersedia" ketika program tersebut

dikompilasi dan ketika program terus dieksekusi. Tentunya lebih pas tergantung dari

lingkungan Java yang kita lakukan. biasanya paket yang bernama alatbantu harus berada di

dalam direktori alatbantu, dan direktori tersebut harus diletakkan pada direktori yang sama

dengan program yang akan menggunakan kelas itu.

Pada program yang menggunakan banyak kelas, maka sangat masuk akal apabila kita

membagi dan mengorganisir kelas tersebut dalam satu atau beberapa paket. Dan juga

masuk akal untuk membuat paket baru sebagai toolbox atau dengan kata lain membuat API

baru yang belum dikover oleh API Java. (Seringkali orang yang membuat API lebih

disanjung akan kehebatannya dibandingkan dengan orang yang hanya menggunakan API).

Dalam program Eclipse kita, kita sudah menggunakan paket dari awal. Bab ini

menjelaskan lebih detail tentang paket. Sebetulnya tidak ada salahnya kita mengorganisir

setiap program yang kita buat sejak awal. Pada saat program kita besar, maka perubahan

yang diperlukan untuk mengorganisir akan lebih signifikan.

Tentang Deklarasi

Page 114: Java With Eclipse

Nama adalah hal yang paling dasar dalam pemrograman. Banyak sekali tetek bengek detail

di dalam deklarasi dan menggunakan nama. Dalam bagian ini akan kita singgung beberapa

hal penting tentang deklarasi dan penggunaan variabel pada Java.

Menggabungkan Inisialisasi dan Deklarasi

Ketika perintah deklarasi variabel dilaksanakan, komputer akan menyiapkan memori untuk

digunakan oleh variabel ini. Memori ini harus diinisialisasi, yaitu diberi suatu nilai awal

sebelum bisa digunakan dalam perintah berikutnya. Pada variabel lokal, pernyataan

deklarasi sering diikuti oleh perintah pemberi nilai awal. Misalnya,

int i; // Deklarasi variabel yang bernama i

i = 0; // Beri nilai awal

Kita bisa juga melakukan deklarasi sekaligus inisialisasi variabel tersebut. Kedua

pernyataan di atas bisa kita gabung menjadi

int i = 0; // Deklarasi variabel i sekaligus memberi nilai awal 0

Komputer akan tetap melakukan perintah di atas dalam dua langkah: deklarasi variabel,

kemudian mengisi nilai awal 0. Nilai awal tidak harus berupa suatu bilangan, tetapi bisa

juga berupa ekspresi yang menghasilkan suatu nilai. Dan juga kita boleh melakukan

inisialisasi beberapa variabel sekaligus dalam 1 baris. Misalnya,

int x = 0, y = 1;

int awal, akhir = 'A'; // Ok, tapi hanya akhir yang akan

diinisialisasi

int N = 3, M = N+2; // Ok, karena N sudah diberi nilai awal sebelum

digunakan oleh M

Fitur ini biasa ditemui pada perulangan for, karena pernyataan 1 baris ini bisa dimasukkan

sebagai variabel kontrol di awal pernyataan for. Karena variabel kontrol biasanya tidak

berhubungan dengan bagian program di luar perulangan, maka akan lebih praktis jika

deklarasi variabel kontrol itu dilakukan di awal perulangan. Misalnya

for ( int i = 0; i < 10; i++ ) {

System.out.println(i);

}

Perlu diingat bahwa ekspresi di atas merupakan singkatan dari blok berikut. Saya sengaja

tambahkan { dan } di awal dan akhir untuk mempertegas bahwa i hanya bersifat lokal di

dalam perulangan yang tidak bisa diakses lagi setelah perulangan selesai.

{

int i;

for ( i = 0; i < 10; i++ ) {

System.out.println(i);

Page 115: Java With Eclipse

}

}

Variabel anggota juga bisa diinisialisasi di mana dia dideklarasikan. Misalnya,

public class Bank {

static double sukuBunga = 0.1; // sama dengan 10%

static long maxPenarikan = 2000000;

.

. // Perintah dan variabel lain

.

}

Variabel anggota statik akan dibuat pada saat kelas mulai dimasukkan memori oleh

interpreter Java, inisialisinya pun dilaksanakan pada saat itu. Pada variabel anggota,

deklarasi dan inisialisasi ini bukan hanya sekedar singkatan dari 2 perintah seperti pada

variabel lokal. Pernyataan deklarasi adalah pernyataan yang bisa dibuat di luar subrutin,

sedangkan inisialisasi tidak bisa dilakukan di luar subrutin. Contoh di bawah tidak

diperbolehkan dalam Java.

public class Bank {

static double sukuBunga;

sukuBunga = 0.1; // ILEGAL, perintah ini harus ada di dalam

subrutin

}

Karenanya, deklarasi variabel anggota biasanya juga dirangkai dengan inisialisasi nilai

awalnya. Jika tidak ada nilai awal yang diberikan, maka nilai awal bawaan akan

digunakan. Misalnya "static int i;" setara dengan "static int i = 0;".

Konstanta dan Sifat "final"

Kadang-kadang dalam kondisi tertentu, kita ingin nilai suatu variabel tidak boleh berubah

setelah diberi nilai awal. Misalnya, pi diinisialisasi dengan nilai 3.14159, maka nilai ini

akan bisa diubah oleh bagian lain dalam program. Di sini mungkin sang programmer ingin

mendefinisikan suatu konstanta yang diberi nama "pi" sebagai pengganti dari bilangan

3.14159.

Dengan cara ini, program akan lebih mudah dimengerti apabila program tersebut ditulis

dalam "keliling = pi*diamater;" daripada "pokok = 3.14159*diameter;".

Dalam java, sifat "final" bisa digunakan pada deklarasi variabel untuk membuat nilai suatu

variabel tidak bisa diubah setelah diinisialisasi. Misalnya

final static double pi = 3.14159;

Page 116: Java With Eclipse

Apabila kita mencoba mengganti isi variabel tersebut, misalnya di tengah program dengan

perintah "pi = 20;" komputer akan menampilkan pesan kesalahan sintaks pada saat

kompilasi.

Sifat "final" boleh diberikan pada variabel lokal dan bahkan pada parameter formal, akan

tetapi paling berguna apabila diberikan pada variabel anggota. Variabel anggota statik yang

dideklarasikan dengan sifat "final" juga disebut sebagai konstanta yang diberi nama.

Program akan jauh lebih mudah dibaca dengan menggunakan konstanta bernama ini jika

diberikan pada sesuatu angka penting yang digunakan dalam program. Gaya penulisan

yang dianjurkan untuk konstanta bernama ini adalah dengan menggunakan huruf besar

dengan baris bawah (jika diperlukan). Misalnya

final static double PI = 3.14159;

Gaya penulisan seperti ini juga digunakan oleh kelas standar Java, yang di dalamnya

terdapat banyak sekali konstanta bernama. Misalnya, konstanta PI sudah didefinisikan Java

dalam kelas Math, yaitu Math.PI, sehingga kita tidak perlu mendefinisikannya sendiri.

Contoh lain adalah memberikan nama untuk gaya huruf, seperti Font.PLAIN, Font.BOLD

dan Font.ITALIC. Konstanta ini digunakan untuk menandai gaya huruf untuk digunakan

oleh subrutin lain dalam kelas Font.

Salah satu alasan utama untuk menggunakan konstanta bernama adalah supaya kita bisa

mengubah isinya dengan cepat apabila di kemudian hari kita ingin memiliki nilai konstanta

yang berbeda. Apabila nilai ini diubah, maka kita harus mengkompilasi ulang program

kita, karena nilai ini tidak bisa diubah ketika program dieksekusi.

Misalnya kita definisikan sukuBunga pada contoh di atas sebagai konstanta bernama, yaitu

static final double SUKU_BUNGA = 0.1;

Suatu saat misalnya 2 tahun kemudian, bank tersebut ingin mengubah suku bunganya

menjadi 5% atau 0.05. Kita bisa mengubah nilainya hanya di satu tempat yaitu di mana

deklarasi SUKU_BUNGA berada. Kita tidak perlu mencari semua nilai 0.1 di dalam

program untuk menggantinya satu per satu. Selain repot, mungkin membingungkan karena

0.1 bisa digunakan untuk nilai lainnya, bukan hanya nilai suku bunga.

Aturan Penamaan dan Jangkauan (scope)

Ketika deklarasi variabel dieksekusi, komputer akan menyediakan tempat di memori untuk

variabel tersebut. Nama variabel dapat digunakan oleh kode program untuk mengacu pada

alamat di mana data tersebut disimpan di dalam memori. Bagian di dalam kode sumber

(yaitu program Java yang kita tulis, masih dalam bentuk yang dimengerti manusia sebelum

dikompilasi menjadi bahasa mesin) di mana variabel tersebut valid dan bisa digunakan

Page 117: Java With Eclipse

disebut jangkauan variabel. Bukan hanya variabel yang memiliki jangkauan, tetapi juga

nama subrutin dan nama parameter formal.

Untuk subrutin statik, jangkauannya lebih mudah dipahami. Jangkauan subrutin statik

adalah kelas di mana ia didefinisikan. Kita bisa juga memanggil subrutin itu dari dalam

kelas dirinya sendiri. Dalam pemrograman tingkat lanjut, teknik ini disebut rekursi

(recursion), yaitu subrutin yang memanggil dirinya sendiri. Teknik ini digunakan dalam

bab yang akan datang yang berbicara detail tentang struktur data dan algoritma.

Untuk variabel anggota suatu kelas, aturannya sama, tetapi dengan sedikit pengecualian.

Kita boleh memiliki variabel lokal atau parameter formal yang namanya sama dengan

variabel anggota static. Jika ini terjadi, maka variabel anggota akan disembunyikan oleh

Java. Dalam contoh berikut :

public class Game {

static int hitung; // variabel anggota

static void mainGame() {

int hitung; // variabel lokal

.

. // Perintah untuk main game

.

}

.

. // Variabel dan subrutin lain

.

} // akhir kelas Game

Kita lihat bahwa dalam subrutin mainGame(), "hitung" mengacu pada dua hal yaitu

variabel lokal dan variabel anggota statik. Di luar kelas Game, "hitung" hanya mengacu

pada variabel anggota statik. Dalam situasi seperti ini, di dalam subrutin mainGame() maka

"hitung" sebagai variabel anggota akan disembunyikan oleh Java. Untuk menggunakan

variabel anggota ini, kita bisa menggunakan nama lengkapnya yaitu Game.hitung. Akan

tetapi trik ini tidak bisa digunakan untuk variabel anggota yang tidak statik.

Jangkauan parameter formal di dalam suatu subrutin berada di dalam blok di mana ia

dideklarasikan. Sehingga pada "for (int i=0; i<10; i++)" variabel i hanya bisa di

lihat di dalam blok perulangan for, tetapi tidak valid di luar perulangan ini meskipun pada

subrutin yang sama.

Akan tetapi kita tidak boleh mendeklarasikan parameter formal dengan nama yang sama

dengan variabel lokal yang berlaku di sana. Misalnya,

Page 118: Java With Eclipse

void subrutinJelek(int y) {

int x;

while (y > 0) {

int x; // ERROR: x sudah didefinisikan sebelumnya

.

.

.

}

}

Dalam bahasa pemrograman lain, pernyataan di atas masih dibolehkan, yaitu dengan

menyembunyikan x pada deklarasi sebelumnya. Akan tetapi pada Java, segera setelah blok

yang memuat suatu variabel selesai, namanya bisa dipergunakan kembali. Misalnya.

void subrutinBagus(int y) {

while (y > 10) {

int x;

.

.

.

// Jangkauan x berakhir di sini

}

while (y > 0) {

int x; •// OK: Deklarasi x sebelumnya sudah kadaluarsa

.

.

.

}

}

Ada beberapa hal lain yang menarik di Java. Nama subrutin dan variabel boleh sama,

karena komputer akan selalu dapat membedakan mana subrutin atau variabel dengan

melihat tanda buka kurung '(' setelah namanya. Variabel tidak pernah menggunakan tanda

kurung setelah namanya. Sehingga suatu kelas boleh memiliki variabel anggota yang

bernama "hitung" dan subrutin yang bernama "hitung()".

Lebih jauh lagi, nama suatu kelas boleh menggunakan nama subrutin atau variabel yang

sudah ada. Komputer bisa melihat apakah suatu nama berupa kelas, variabel atau subrutin.

Kelas adalah juga tipe data, sehingga suatu kelas bisa digunakan untuk mendeklarasikan

variabel dengan tipe kelas tersebut, atau membuat subrutin yang keluarannya bertipe kelas

tersebut.

Atau sebagai contoh, perintah berikut bisa diterima oleh program Java :

Page 119: Java With Eclipse

static Gila Gila(Gila Gila) {

.

.

.

}

Gila pertama adalah tipe keluaran suatu fungsi. Gila kedua adalah nama subrutin. Yang

ketiga adalah tipe data parameter, dan keempat adalah nama parameter formal.

Tetapi, ingat tidak semua yang mungkin dan boleh dilakukan berarti harus dilakukan.

Lihat lebih jelas di sini : !newsletter_url

Bab VI - Pemrograman Berorientasi Objek

Jika subrutin hanya melambangkan suatu tugas, objek merupakan gabungan data (dalam

bentuk variabel instansi - atau "instance variable") dan beberapa tugas atau disebut

"perilaku" terhadap kumpulan data tersebut (dalam bentuk metode instansi - atau

"instance method"). Oleh karena itu objek merupakan stuktur baru yang dapat menangani

semakin rumitnya kompleksitas suatu program

Bagian ini meliputi pembuatan dan penggunaan objek dalam bahasa Java. Juga akan

dibahas inti tentang pemrograman berorientasi objek, yaitu : pewarisan dan poli morfisme

(perubahan wujud).

Objek, Metode Instansi dan Variable Instansi

Pemrograman berorientasi objek bermaksud untuk memecahkan masalah programming

mirip dengan cara berfikir manusia dan bagaimana manusia berinteraksi dengan dunia.

Kita telah melihat bagaimana masalah programming dipecahkan dengan membaginya

menjadi instruksi-instruksi kecil yang dirangkai menjadi suatu kesatuan tugas. Cara ini

disebut juga dengan pemrograman berdasarkan prosedur.

Pada pemrograman berorientasi objek, kita membagi masalah pemrograman berdasarkan

objek atau "sesuatu" benda. Objek ini diibaratkan seperti makhluk hidup, yaitu memiliki

ciri-ciri fisik dan juga perilaku. Dalam pemrograman berorientasi objek, ciri-ciri fisik ini

disebut juga sebagai atribut, pada dasarnya berisi informasi tentang objek tersebut.

Sedangkan perilaku disebut sebagai metode, yaitu bagaimana suatu objek bertindak atau

melakukan sesuatu.

Dengan cara ini diharapkan pemrograman berorientasi objek merupakan pemrograman

yang lebih alami dibandingkan dengan pemrograman berorientasi prosedur, karena cara

fikir kita sudah terbiasa dengan bagaimana kita berinteraksi dengan dunia.

Page 120: Java With Eclipse

Sampai tingkat tertentu, PBO hanya merubah cara pandang programmer. Objek dalam

kaitannya dengan dasar pemrograman adalah gabungan dari variabel (dalam hal ini atribut)

dan subrutin (yaitu metode) yang berinteraksi dengan variabel pada objek.

Banyak bahasa pemrograman lain yang juga mendukung konsep PBO, akan tetapi Java

merupakan bahasa yang mendukung penuh PBO, di antaranya karena Java memiliki

beberapa fitur penting yang berbeda dari bahasa standar. Untuk menggunakan fitur ini, kita

harus mengubah cara pandang kita terlebih dahulu.

Objek terkait erat dengan kelas. Kita telah bahas dan mencoba beberapa contoh kelas pada

bab-bab sebelumnya. Seperti kita lihat pula bahwa kelas memiliki variabel dan subrutin.

Jika objek juga memiliki variabel dan subrutin, lalu apa bedanya dengan kelas? Lalu

mengapa kita harus memiliki cara pandang berbeda dengan cara kita melihat kelas?

Sepertinya apabila kita lihat contoh-contohnya sebelumnya, perbedaannya hanya

menghilangkan kata "static" dari deklarasi variabel dan subrutinnya, khan?

Seperti disebutkan sebelumnya bahwa kelas mendeskripsikan suatu objek, atau lebih

tepatnya, bagian non "static" mendeskripsikan suatu objek. Mungkin bahasa biologi,

kelas adalah species, sedangkan objek merupakan individu. Human sapiens (nama biologi

manusia) adalah kelas, sedangkan Anda, saya, ibu, bapak, pak hansip adalah objek.

Masing-masing dari kita memiliki "blueprint" atau cetak biru yang sama, tetapi kita

memiliki ciri-ciri yang berbeda, rambut, mata, telinga, tinggi badan, berat badan, dan juga

perilaku kita saat makan, minum, belajar, dan lain-lain.

Dalam sudut pandang pemrograman, kelas digunakan untuk menciptakan suatu objek.

Atau dengan kata lain, kelas adalah pabrik pembuat objek. Bagian non-statik dari suatu

kelas adalah bagian yang memuat detail suatu objek, yaitu apa isi variabel dan metodenya.

Perbedaan kelas dan objek adalah : Objek diciptakan dan dihancurkan ketika program

berjalan, sehingga kita bisa memiliki beberapa objek sekaligus.

Kita lihat contoh sederhana, dimana kelas sederhana digunakan untuk menggabungkan

beberapa variabel statik. Misalnya

class DataPenduduk {

static String nama;

static int umur;

}

Pada program yang menggunakan kelas ini, hanya ada satu kopi dari setiap variabel

DataPenduduk.nama dan DataPenduduk.umur. Hanya akan ada satu penduduk, karena

kita menyimpan data tersebut sebagai data statik, yang artinya hanya satu tempat di

Page 121: Java With Eclipse

memori di mana data tersebut disimpan. Kelas DataPenduduk dan variabel isinya akan ada

selama program tersebut berjalan.

Sekarang kita lihat kode berikut yang memiliki variabel non-statik:

class DataPenduduk {

String nama;

int umur;

}

Dalam hal ini tidak ada lagi yang variabel DataPenduduk.nama dan DataPenduduk.umur,

karena nama dan umur bukan anggota statik kelas DataPenduduk. Jadi, tidak ada yang bisa

kita lakukan dengan kelas ini sama sekali, kecuali membuat objek dari kelas ini.

Setiap objek akan memiliki variable sendiri yang dinamakan "nama" dan "umur". Kita bisa

membuat banyak "penduduk" karena kita bisa membuat objek untuk setiap penduduk.

Misalnya setiap kali seseorang dilahirkan, kita bisa membuat objek penduduk baru. Atau

jika seseorang meninggal dunia, kita bisa hapus objek tersebut.

Suatu sistem yang terdiri dari kumpulan objek digunakan untuk memodelkan apa yang

terjadi di alam nyata. Kita tidak bisa membuat beberapa objek dengan menggunakan

variabel statik.

Contoh lainnya adalah pada GUI. Bayangkan kita memiliki beberapa tombol, misalnya

tombol OK, Cancel dan Apply. Masing-masing tombol ini memiliki kelas yang sama akan

tetapi apa yang dilakukan tombol ini berbeda-beda. Teks yang ditampilkan (OK, Cancel

atau Apply) merupakan variabel masing-masing objek.

Suatu objek yang diciptakan dari suatu kelas disebut instansi dari kelas tersebut. Variabel

yang dimiliki oleh objek disebut variabel instansi. Sedangkan subrutinnya disebut .

(Dalam PBO subrutin disebut metode)

Misalnya dalam kelas DataPenduduk di atas, kemudian kita buat suatu objek dari kelas ini,

maka objek yang diciptakan disebut instansi dari kelas DataPenduduk, "nama" dan "umur"

adalah variabel instansi di dalam objek tersebut.

Penting untuk diingat bahwa kelas suatu objek menentukan tipe data dari variabel instansi,

akan tetapi isi datanya sendiri tidak disimpan di dalam kelas, akan tetapi di dalam objek

yang diciptakan, sehingga setiap objek akan memiliki data masing-masing.

Begitu juga dengan metode instansi, misalnya pada kelas tombol, kita memiliki metode

yang dinamakan klik(). Masing-masing tombol akan melakukan tugas berbeda-beda

tergantung dari objeknya.

Page 122: Java With Eclipse

Seperti kita lihat di sini bahwa bagian statik dan non-statik dari suatu kelas merupakan hal

yang sama sekali berbeda. Banyak kelas yang hanya memiliki anggota statik, atau hanya

memiliki anggota non-statik. Akan tetapi kita juga bisa mencampur keduanya dalam suatu

kelas. Variabel anggota statik suatu kelas juga bisa disebut variabel kelas dan metode

anggota statik suatu kelas juga bisa disebut metode kelas, karena mereka adalah milik kelas

dan bukan milik objek yang diciptakan dari suatu kelas.

Sekarang kita gunakan contoh yang lebih real. Kita akan buat versi sederhana dari kelas

Murid, dimana kita akan menyimpan informasi tentang murid yang terdaftar pada suatu

sekolah.

class Murid {

String nama ; // nama murid

double nilai1, nilai2, nilai3; // nilai ulangan murid tersebut

double hitungRataRata() { // hitung rata-rata nilai ulangan

return (nilai1 + nilai2 + nilai3) / 3;

}

}

Kita lihat bahwa semua anggota kelas tersebut bukan anggota statik, artinya kelas ini hanya

bisa digunakan untuk membuat objek. Definisi kelas ini artinya bahwa di dalam objek yang

akan diciptakan, akan ada variabel instansi yang bernama nama, nilai1, nilai2, dan

nilai3, dan juga metode instansi yang bernama hitungRataRata(). Setiap murid

memiliki nilai rata-rata yang berbeda-beda. (Makanya ini disebut perilaku suatu objek

berbeda-beda).

Dalam Java, kelas merupakan tipe data, yaitu mirip dengan tipe data bawaan seperti int

atau boolean. Jadi nama kelas bisa digunakan untuk menentukan tipe suatu variabel dalam

deklarasi pernyataan, dalam parameter formal dan juga dalam tipe keluaran suatu fungsi.

Misalnya, program mendefinisikan seorang murid dengan pernyataan seperti :

Murid amir;

Akan tetapi membuat variabel seperti di atas TIDAK menciptakan objek. Initinya, ini

adalah hal Yang Sangat Amat Penting :

Dalam Java, tidak ada variabel yang bisa menyimpan objek. Variabel hanya bisa

menyimpan referensi (alamat di memori) suatu objek. Komputer akan menggunakan

referensi ini untuk mencari objek di dalam memori.

Objek diciptakan dengan pernyataan new, yang bertugas menciptakan objek kemudian

mengembalikan referensi ke objek yang sudah diciptakan. Misalnya amir adalah variabel

dengan tipe Murid seperti dideklarasikan di atas, maka pernyataan berikut :

Page 123: Java With Eclipse

amir = new Murid();

akan membuat objek yang merupakan instansi dari kelas Murid. Variabel amir akan

menyimpan referensi ke objek yang baru saja diciptakan.

Sekarang anggap variabel amir merujuk pada objek yang diciptakan dari kelas Murid.

Dalam objek tersebut terdapat variabel nama, nilai1, nilai2, dan nilai3. Variabel

instansi ini bisa dipanggil dengan amir.nama, amir.nilai1, amir.nilai2, dan

amir.nilai3. (Ingat aturan penulisan nama lengkap, akan tetapi karena kelas ini tidak

memiliki anggota statik, dan hanya objek yang diciptakan dari kelas ini memiliki variabel

atau metode ini, maka nama lengkapnya diturunkan dari nama objek yang memilikinya).

Misalnya, program berikut :

System.out.println("Hai, " + amir.nama + " . Nilai Anda adalah : ");

System.out.println(amir.nilai1);

System.out.println(amir.nilai2);

System.out.println(amir.nilai3);

Program di atas akan mencetak nama dan nilai-nilai yang disimpan oleh objek amir.

Begitu juga kita bisa menghitung rata-rata pada suatu objek dengan menggunakan

amir.hitungRataRata(). Sehingga untuk menghitung rata-rata murid tersebut, bisa kita

perintahkan dengan:

System.out.println("Nilai rata-rata Anda adalah : " +

amir.hitungRataRata());

Lebih umum lagi, kita bisa menggunakan amir.nama seperti layaknya variabel bertipe

String, artinya kita bisa menghitung jumlah karakter dengan menggunakan

amir.nama.length().

Kita juga bisa membuat variabel seperti amir untuk tidak menunjuk atau memiliki

referensi ke obek mana pun. Dalam hal ini kita sebut bahwa objek amir berisi referensi

kosong (null reference). Referensi kosong ini ditulis dalam Java dengan ekspresi "null".

Dan kita bisa menyimpan nilai "null" pada variabel amir dengan perintah :

amir = null;

Dan kita juga bisa uji apakah amir berisi referensi kosong dengan perintah

if (amir == null) . . .

Jika suatu variabel berisi referensi kosong, maka tentu saja, kita tidak bisa mengambil

variabel instansi dan menjalankan metode instansi, karena tidak ada objek yang dirujuk

pada variabel tersebut. Misalnya jika variabel amir berisi null, maka kita tidak bisa

mengambil variabel amir.nilai1.

Page 124: Java With Eclipse

Jika program kita mencoba mengakses referensi kosong secara ilegal, maka di tengah-

tengah jalannya program, program akan menampilkan pesan kesalahan "null pointer

exception".

Mari kita lihat beberapa pernyataan yang bisa digunakan dengan objek :

Murid mrd1, mrd2, mrd3, mrd4; // mendeklarasikan 4 variabel yang

bertipe Murid

mrd1 = new Murid(); // membuat objek baru dari kelas Murid,

kemudian menyimpan referensinya pada variabel mrd1

mrd2 = new Murid(); // membuat objek baru dari kelas Murid,

kemudian menyimpan referensinya pada variabel mrd2

mrd3 = mrd1; // menkopi „referensi" yang disimpan

pada mrd1 ke mrd3

mrd4 = null; // menyimpan referensi kosong ke mrd4

mrd1.nama = "Ahmad Surahmat"; // mengisi nilai variabel instansi

mrd2.nama = "Hamid Samsudin";

Setelah komputer menjalankan program tersebut, maka kira-kira memori komputer akan

tampak seperti gambar berikut :

Page 125: Java With Eclipse

Gambar tersebut menunjukkan variabel dalam kotak-kotak kecil dengan nama variabelnya.

Objek ditunjukkan dalam kotak dengan pojok bulat. Ketika suatu variabel berisi referensi

ke suatu objek, maka nilainya adalah seperti panah yang menunjuk pada objek tersebut.

Variabel mrd4 bernilai null, sehingga tidak menunjuk ke mana-mana. Panah dari mrd1

dan mrd3 menjunjuk pada objek ang sama. Ini merupakan Hal Yang Sangat Penting :

Jika isi variabel suatu objek diberikan kepada variabel yang lain, maka yang dikopi

hanya referensinya saja. Isi objek tidak pernah dikopi.

Ketika pernyataan "mrd3 = mrd1;" dieksekusi, tidak ada objek baru yang dibuat. Akan

tetapi mrd3 akan merujuk pada alamat yang sama seperti mrd1. Konsekuensinya mungkin

sedikit mengejutkan. Misalnya variabel mrd1.nama dan mrd3.nama menunjuk pada

variabel yang persis sama, maka apabila mrd1.nama disi dengan "Juju Juminten", maka

mrd3.nama juga berisi "Juju Juminten".

Sekali lagi, variabel tidak berisi objek, akan tetapi berisi referensi ke suatu objek.

Kita bisa menguji beberapa objek dengan operator == dan != untuk menguji kesamaan dan

ketidaksamaan. Akan tetapi yang dicek lagi-lagi bukan isi objek, melainkan alamat memori

dimana objek tersebut dijadikan referensi. Jika alamat referensi di memori sama, artinya

kedua objek tersebut merujuk pada alamat memori yang sama. Ini berarti perubahan yang

dilakukan pada variabel yang satu akan ikut mempengaruhi variabel yang lain.

Untuk menguji isinya, maka isinya harus dibandingkan satu per satu, yaitu misalnya

dengan "mrd3.nilai1 == mrd1.nilai1 && mrd3.nilai2 == mrd1.nilai2 &&

mrd3.nilai3 == mrd1.nilai3 && mrd3.nama.equals(mrd1.nama)"

Seperti telah disebutkan sebelumnya bahwa String juga merupakan objek. Sehingga

membandingkan String yang satu dengan String yang lain dengan menggunakan tanda ==

adalah membandingkan apakah alamat memori yang ditunjuk oleh String yang satu sama

dengan alamat memori yang ditunjuk oleh String yang lain. Ingat == pada objek bukan

membandingkan isi objek, tetapi membandingkan alamat memori yang ditunjuk oleh

variabel tersebut. Untuk membandingkan isi String, kita dapat menggunakan

String.equals() di mana parameternya adalah objek String yang akan dibandingkan.

Metode ini akan membandingkan karakter per karakter dari kedua String.

Misalnya, variabel salam berisi String "Selamat". Untuk menguji apakah variabel salam

berisi "Selamat", bisa kita gunakan perintah

salam.equals("Selamat")

Konsekuensi lainnya adalah apabila suatu variabel merujuk pada objek yang

dideklarasikan sebagai final. Ini berarti nilai variabel itu tidak bisa berubah setelah

Page 126: Java With Eclipse

diinisialisasi, sedangkan isi variabel itu adalah rujukan ke alamat memori tempat objek

berada. Dengan demikian variabel tersebut akan selalu menunjuk pada alamat memori

yang sama. Akan tetapi hal ini tidak berlaku untuk objek itu sendiri. Apabila ada variabel

lain yang tidak "final" tetapi menunjuk pada alamat memori yang sama, kemudian

variabel ini mengubah isi objek tersebut, maka isi objek tersebut bisa berubah.

Kita boleh saja untuk menulis perintah sebagai berikut.

final Murid mrd = new Murid();

murid.nama = "Ahmad Basir";

Perhatikan pada kode di atas bahwa isi objek bisa diubah. Akan tetapi jika kita mencoba

menulis seperti ini

Murid mrd99 = new Murid();

mrd = mrd99; // ILEGAL, karena mrd adalah variabel final

Konstruktor dan Inisialisasi Objek

Kelas pada Java memiliki sifat yang sama sekali berbeda dengan tipe data primitif lainnya,

seperti int atau boolean. Seperti disebutkan pada bagian sebelumnya, mendeklarasikan

suatu variabel dengan tipe suatu kelas tidak berarti membuat objek dari kelas tersebut.

Objek tersebut harus dibuat (constructed). Pada saat objek dibuat, komputer akan mencari

tempat yang tidak dipakai pada memori heap untuk menempatkan objek tersebut,

kemudian mengisi objek itu dengan variabel instansi. Sebagai programmer, kita tidak

peduli dengan bagaimana suatu objek disimpan, akan tetapi kita ingin mengatur supaya

nilai yang disimpan pada saat objek tersebut dibuat sesuai dengan keinginan kita. Dalam

beberapa kasus, kita bahkan ingin suatu objek melakukan tugas tertentu untuk pertama kali

begitu ia diciptakan.

Variabel instansi dapat diisi dengan nilai awal pada saat ia dideklarasikan, seperti variabel

lainnya. Misalnya, kelas berikut yang bernama PasanganDadu, yang melambangkan

sepasang dadu. Kelas ini memiliki dua variabel instansi yang melambangkan nilai yang

ditunjukkan oleh masing-masing dadu dan metode instansi untuk mengocok dadu, yaitu :

class PasanganDadu {

public int dadu1 = 3; // Angka pada dadu pertama

public int dadu2 = 4; // Angka pada dadu kedua

public void kocok() {

// Kocok dadu dengan menggunakan bilangan acak antara 1 dan 6

dadu1 = (int)(Math.random()*6) + 1;

dadu2 = (int)(Math.random()*6) + 1;

}

Page 127: Java With Eclipse

} // akhir kelas PasanganDadu

Variabel instansi dadu1 dan dadu2 diisi dengan nilai awal 3 dan 4. Inisialisasi ini

dilakukan setiap kali objek PasanganDadu dibuat. Ingat bahwa kelas PasanganDadu hanya

1, akan tetapi kita bisa membuat banyak objek dari kelas ini. Setiap kali objek dibuat,

objek tersebut memiliki tempat di memori sendiri, yang disebut dengan instansi objek

tersebut. Perintah "dadu1 = 3" dan "dadu2 = 4" akan dieksekusi setiap kali objek dibuat.

Kita bisa memodifikasi kelas PasanganDadu dengan nilai awal acak, bukan 3 dan 4

misalnya, dengan potongan kode berikut :

class PasanganDadu {

public int dadu1 = (int)(Math.random()*6) + 1; // Angka pada dadu

pertama

public int dadu2 = (int)(Math.random()*6) + 1; // Angka pada dadu

kedua

public void kocok() {

// Kocok dadu dengan menggunakan bilangan acak antara 1 dan 6

dadu1 = (int)(Math.random()*6) + 1;

dadu2 = (int)(Math.random()*6) + 1;

}

} // akhir kelas PasanganDadu

Karena inisialisasi dilakukan setiap kali objek dibuat, maka setiap objek akan memiliki

nilai yang berbeda-beda hasil dari instruksi Math.random() pada inisialisasi variabel

instansi. Untuk inisialisasi variabel static, hal ini tidak mungkin dilakukan, karena hanya

ada 1 variabel statik untuk 1 kelas, tidak peduli berapa pun banyaknya objek yang dibuat.

Jika variabel instansi tidak kita beri nilai awal, maka nilai bawaan akan diberikan secara

otomatis. Tipe data numerik (int, double, dll) memiliki nilai bawaan 0; boolean bernilai

awal false; dan char bernilai awal karakter dengan kode Unicode 0. Variabel instansi juga

bisa bertipe suatu objek. Dalam hal ini, variabel tersebut bernilai awal null. (Ingat bahwa

String adalah objek, sehingga nilai awalnya adalah null).

Objek dibuat dengan operator new, misalnya program yang ingin menggunakan objek

PasanganDadu dapat menggunakan perintah berikut :

// deklarasi variabel dan pembuatan objek dilakukan sekaligus

PasanganDadu dadu = new PasanganDadu();

Pada contoh di atas, new PasanganDadu() adalah perintah untuk membuat objek,

meletakkannya di memori dan menyimpan alamat objek ini di memori pada variabel dadu.

Bagian ekspresi PasanganDadu() mirip seperti memanggil subrutin. Sebetulnya itulah

Page 128: Java With Eclipse

yang terjadi, yaitu program memanggil subrutin spesial yang dinamakan konstruktor

(constructor). Mungkin Anda heran karena kita tidak melihat adanya subrutin bernama

PasanganDadu(). Akan tetapi semua kelas memiliki konstruktor, yang jika kita tidak buat

secara khusus, Java akan menambahkannya secara otomatis, yang disebut konstruktor

bawaan.

Konstruktor bawaan melakukan hal-hal umum seperti mengalokasi memori, mengisi nilai

variabel instansi dengan nilai bawaannya, dan mengembalikan alamat objek yang dibuat di

memori. Jika kita menginginkan hal lain yang ikut dilaksanakan ketika suatu objek dibuat,

maka kita harus membuat konstruktor sendiri.

Bagaimana cara mendefinisikan konstruktor? Konstruktor dideklarasikan mirip dengan

deklarasi subrutin, dengan 3 perkecualian. Konstruktor tidak memiliki tipe keluaran (void

pun tidak dibolehkan. Namanya harus sama dengan nama kelas di mana ia dideklarasikan.

Sifat yang bisa digunakan hanya sifat akses, yaitu public, private, dan protected (static

tidak diperbolehkan).

Di lain pihak, konstruktor memiliki blok yang terdiri dari kumpulan perintah seperti pada

subrutin. Kita bisa menggunakan perintah apapun seperti pada subrutin biasa, termasuk

memiliki satu atau lebih parameter formal. Sebetulnya salah satu alasan untuk

menggunakan parameter adalah kita bisa membuat beberapa konstruktor yang menerima

data dalam berbagai bentuk, sehingga objek yang kita buat bisa dinisialisasi dengan cara

yang berbeda-beda sesuai dengan kondisi dan kebutuhan dari program yang akan kita buat.

Misalnya, kelas PasanganDadu di atas kita ubah sehingga kita bisa memberi nilai awal

sendiri. Dalam hal ini kita buat konstruktor yang menerima 2 nilai sebagai nilai awal dadu,

yaitu nilai1 dan nilai2.

class PasanganDadu {

public int dadu1; // Angka pada dadu pertama

public int dadu2; // Angka pada dadu kedua

public PasanganDadu(int nilai1, int nilai2) {

// Konstruktor, mengambil nilai1 dan nilai2 sebagai nilai awal

untuk dadu1 dan dadu2

dadu1 = nilai1;

dadu2 = nilai2;

}

public void kocok() {

// Kocok dadu dengan menggunakan bilangan acak antara 1 dan 6

Page 129: Java With Eclipse

dadu1 = (int)(Math.random()*6) + 1;

dadu2 = (int)(Math.random()*6) + 1;

}

} // akhir kelas PasanganDadu

Konstruktor dideklarasikan dalam bentuk "public PasanganDadu(int nilai1, int nilai2) ...",

tanpa tipe keluaran dan dengan nama yang sama dengan nama kelas. Ini adalah cara Java

mengenal suatu konstruktor dan membedakannya dengan subrutin biasa. Konstruktor ini

memiliki 2 parameter yang harus diisi ketika konstruktor dipanggil. Misalnya,

PasanganDadu dadu = new PasanganDadu(1,2);

Membuat objek baru yang variabel intansinya dadu1 dan dadu2 bernilai 1 dan 2.

Karena sekarang kita telah membuat konstruktor, kita tidak bisa lagi membuat objek

dengan perintah "new PasanganDadu()". Java akan membuat konstruktor bawaan apabila

tidak ada satupun konstruktor yang didefinisikan. Akan tetapi, kita bisa membuat

konstruktor lain di kelas tersebut, karena suatu kelas bisa terdiri dari beberapa konstruktor

asalkan parameternya berbeda.

Sekarang kita akan modifikasi lagi kelas tersebut dengan 2 konstruktor, yang mana apabila

tidak ada parameter yang diberikan, maka objek tersebut akan mengisi nilai dadu1 dan

dadu2 dengan bilangan acak.

class PasanganDadu {

public int dadu1; // Angka pada dadu pertama

public int dadu2; // Angka pada dadu kedua

public PasanganDadu() {

// Isi dadu1 dan dadu2 dengan bilangan acak, dengan memanggil

metode

// kocok()

kocok();

}

public PasanganDadu(int nilai1, int nilai2) {

// Konstruktor, mengambil nilai1 dan nilai2 sebagai nilai awal

untuk

// dadu1 dan dadu2

dadu1 = nilai1;

dadu2 = nilai2;

}

public void kocok() {

Page 130: Java With Eclipse

// Kocok dadu dengan menggunakan bilangan acak antara 1 dan 6

dadu1 = (int) (Math.random() * 6) + 1;

dadu2 = (int) (Math.random() * 6) + 1;

}

} // akhir kelas PasanganDadu

Sekarang kita bisa memilih bagaimana membuat objek, yaitu dengan "new

PasanganDadu()" atau dengan "new PasanganDadu(x,y)", di mana x dan y adalah

ekpresi bernilai int.

Kelas ini, bisa digunakan dalam program lain yang membutuhkan sepasang dadu. Program

tersebut tidak lagi harus memanggil fungsi

(int)(Math.random()*6) + 1

karena perintah ini sudah diimplementasikan di dalam kelas PasanganDadu. Bagi

programmer, teknik seperti ini mempermudah pemecahan masalah dengan

mengelompokkannya menjadi ciri dan perilaku suatu objek.

Berikut ini adalah contoh program lain yang menggunakan objek PasanganDadu() untuk

menghitung berapa kali kocok akan menghasilkan jumlah nilai yang sama. Program

lengkapnya bisa diunduh di sini untuk diimport ke dalam Eclipse.

public class KocokDuaPasangDadu {

/**

* @param args

*/

public static void main(String[] args) {

PasanganDadu daduPertama = new PasanganDadu(); // pasangan dadu

pertama

PasanganDadu daduKedua = new PasanganDadu(); // pasangan dadu

kedua

int jumlahKocokan; // untuk mengitung berapa kali dua pasang dadu

// tersebut dikocok

int total1; // hitung berapa kali dadu pertama muncul

int total2; // hitung berapa kali dadu kedua muncul

jumlahKocokan = 0;

do {

daduPertama.kocok(); // kocok dadu pertama

total1 = daduPertama.dadu1 + daduPertama.dadu2; // hitung

jumlahnya

Page 131: Java With Eclipse

System.out.println("Pasangan dadu pertama berjumlah " +

total1);

daduKedua.kocok(); // kocok dadu pertama

total2 = daduKedua.dadu1 + daduKedua.dadu2; // hitung

jumlahnya

System.out.println("Pasangan dadu pertama berjumlah " +

total2);

jumlahKocokan++;

System.out.println(); // cetak baris kosong

} while (total1 != total2);

System.out.println("Butuh " + jumlahKocokan

+ " kocokan hingga pasangan "

+ " dadu pertama dan kedua berjumlah sama");

}

}

Keluarannya adalah sebagai berikut

Page 132: Java With Eclipse

Konstruktor adalah subrutin, tetapi bukan subrutin biasa, dan bukan metode instansi,

karena konstruktor tidak dimiliki oleh suatu objek. Karena tugasnya membuat objek, maka

konstruktor dijalankan sebelum objek dibuat. Konstruktor mirip dengan subrutin anggota

statik, tetapi dia tidak bisa dideklarasikan "static". Bahkan menurut spesifikasi Java,

konstruktor bukan anggota suatu kelas sama sekali.

Tidak seperti subrutin lain, konstruktor hanya bisa dipanggil melalui operator "new",

dalam bentuk

new nama_kelas(parameter);

di sini "parameter" boleh kosong. Hasil keluarannya adalah alamat memori di mana objek

yang baru dibuat tersebut disimpan. Seringkali, kita akan simpan hasil keluarannya di

dalam suatu variabel, atau bisa juga hasil keluarannya diberikan ke dalam suatu fungsi

sebagai parameter.

Memanggil konstruktor lebih rumit daripada memanggil subrutin atau fungsi biasa. Hal-hal

berikut sangat membantu untuk lebih memahami apa yang dilakukan oleh konstruktor

ketika ia dipanggil untuk membuat suatu objek:

1. Pertama, komputer memberi daerah pada memori yang tidak digunakan, cukup

untuk dipakai oleh objek yang akan dibuat

2. Komputer akan mengisi variabel instansi objek tersebut dengan nilai bawaannya.

Jika deklarasi variabel instansi pada kelas memiliki nilai awal tertentu, maka nilai

tersebut akan dimasukkan sebagai nilai awalnya.

3. Parameter aktual pada konstruktor (jika ada) akan dievaluasi dan nilainya diberikan

kepada parameter formal konstruktor tersebut.

4. Perintah pada konstruktor (jika ada) akan dilaksanakan.

5. Referensi objek akan dikembalikan kepada si pemanggil.

Hasil keluarannya adalah referensi ke objek yang baru saja dibuat. Kita bisa gunakan

referensi ini untuk mengambil data pada variabel instansi objek tersebut atau memanggil

metode instansinya.

Contoh lain, mari kita ganti kelas Murid pada bagian sebelumnya. Kita akan tambahkan

konstruktor dan juga kita ganti variabel instansi "nama" menjadi bersifat privat.

class Murid {

private String nama; // Nama murid

public double nilai1, nilai2, nilai3; // Nilai-nilai ujian

Murid(String namaMurid) {

// Konstruktor objek Murid

Page 133: Java With Eclipse

nama = namaMurid;

}

public String getNama() {

// Metode untuk mengambil variabel anggota yang bersifat private

// misalnya variabel instansi nama

return nama;

}

public double hitungRataRata() {

// Hitung rata-rata ulangan

return (nilai1 + nilai2 + nilai3) / 3;

}

} // akhir kelas Murid

Objek bertipe Murid berisi informasi tentang murid tertentu. Konstruktor kelas ini

memiliki parameter bertipe String yaitu nama murid yang akan kita buat. Objek bertipe

Murid ini bisa dibuat dengan pernyataan seperti:

mrd1 = new Murid("Ahmad Surahmat");

mrd2 = new Murid("Hamid Samsudin")

Pada versi aslinya, isi variabel nama harus diisi dengan perintah terpisah setelah objek

dibuat. Masalahnya programmer tidak selalu ingat untuk mengisi nilai nama. Pada versi

baru di atas, setiap kali kita membuat objek, parameter namaMurid harus disertakan, karena

ini dideklarasikan pada konstruktornya. Dengan demikian potensi bug karena kelalaian

programmer dapat dihilangkan dengan mudah.

Contoh keamanan lainnya adalah dengan membuat variabel instansi nama bersifat private.

Ini berarti variabel ini tidak bisa diakses oleh dunia luar secara langsung. Variabel ini

hanya bisa diambil nilainya dengan metode instansi getNama, dan karena tidak bisa diakses

langsung dari luar, maka isi variabel ini tidak bisa diganti dari luar kelas. Sekali objek

Murid dibuat, maka namanya tidak bisa diganti selama murid tersebut ada.

Pemulung Memori (Garbage Collector)

Hingga saat ini kita hanya berbicara tentang membuat objek. Lalu bagaimana menghapus

objek? Pada bahasa pemrograman Java, destruksi (lawan konstruksi yang berarti

menghancurkan) objek dilakukan secara otomatis.

Page 134: Java With Eclipse

Objek dibuat di dalam memori heap, dan bisa diakses hanya melalui referensi yang

disimpan oleh variabel. Apa yang akan dilakukan jika variabel tersebut hilang, misalnya

selesai melakukan tugas dalam subrutin, sehingga referensi ke objek tersebut juga hilang?

Perhatikan contoh berikut ini.

Murid mrd = new Murid("Joko Susanto");

mrd = null;

Di baris pertama, referensi objek baru akan disimpan pada variabel mrd. Pada baris

berikutnya, isi variabel mrd diisi dengan null (atau referensi kosong), sehingga referensi ke

objek yang baru kita buat menjadi hilang. Dalam kondisi seperti ini, komputer tidak bisa

lagi menunjuk kepada objek yang baru dibuat tersebut, karena refernsinya hilang. Atau

dengan kata lain, objek tersebut tidak akan pernah bisa dipakai lagi.

Java menggunakan prosedur yang dinamakan pemulung memori (garbage collector) untuk

mengambil memori di mana suatu objek tidak lagi bisa diakses oleh program. Sistem

komputer harus bertanggung jawab terhadap pengaturan memori, bukan programmer,

untuk melacak objek yang menjadi "sampah". Pada contoh di atas, sangat mudah dilihat

bahwa objek Murid telah menjadi sampah. Biasanya dalam kenyataan sehari-hari, sangat

sulit untuk melacak mana objek sampah dan mana yang ukan. Jika suatu objek telah selesai

digunakan, mungkin akan ada beberapa variabel yang masih menyimpan referensi ke objek

tersebut. Suatu objek baru menjadi sampah apabila semua referensi yang merujuk pada

objek tersebut hilang.

Dalam beberapa bahasa pemrograman lain, programmer diharuskan untuk mengatur

sampahnya sendiri. Akan tetapi, mencoba mengatur penggunaan memori secara manual

sangat sulit, dan sering menimbulkan bug yang tidak terduga. Programmer mungkin tidak

sengaja menghapus objek tersebut, meskipun ada variabel lain yang masih merujuk pada

objek tersebut. Ini disebut kesalahan pointer tak bertuan, dan kesalahannya akan fatal

apabila objek yang akan diakses tak lagi berada di alamat memori tersebut.

Tipe kesalahan lain adalah kebocoran memori, yang mana programmer lupa menghapus

objek yang tidak lagi digunakan. Ini akan berakibat pada penuhnya memori dengan

sampah yang tidak bisa lagi diakses karena referensinya telah hilang. Jika dibiarkan, maka

lambat laun seluruh memori komputer akan habis, sehingga komputer bisa berhenti total.

Karena Java memiliki pemulung memori, kesalahan seperti itu tidak mungkin terjadi.

Pemulung memori sudah dibicarakan sejak lama, dan sudah digunakan pada beberapa

bahasa pemrograman sejak tahun 1960-an. Anda mungkin bertanya kenapa tidak semua

bahasa pemrograman menggunakan pemulung memori? Dulu, pemulung memori

Page 135: Java With Eclipse

membutuhkan waktu pemrosesan yang lama, sehingga penggunaannya akan mengurangi

kinerja program secara keseluruhan. Akan tetapi riset terbaru telah menemukan teknik

pemulungan memori yang lebih canggih, dan ditambah dengan kecepatan komputer yang

makin tinggi, pemulung memori menjadi suatu hal yang sangat realistis.

Berterima kasihlah kepada Java.

Pewarisan, Polimorfisme, dan Kelas Abstrak

Kelas melambangkan cetak biru objek yang memiliki kesamaan struktuk dan perilaku.

Kelas menentukan struktur suatu objek melalui variabel yang terkandung dalam setiap

objek, dan menentukan perilaku melalui metode instansi yang merupakan perilaku suatu

objek.

Ide utama dari pemrograman berorientasi objek -- yang membedakannya dari

pemrograman tradisional -- adalah menciptakan kelas yang memiliki hanya beberapa

(tidak semua) struktur dan perilaku. Kemiripan ini diekspresikan dalam bentuk pewarisan

dan polimorfisme (perubahan bentuk).

Istilah pewarisan berarti suatu kelas bisa mewariskan sebagian atau keseluruhan struktur

dan perilaku kelas lain. Jika kelas B adalah kelas turunan dari kelas A, maka kita bisa juga

menyebut kelas A adalah kelas super dari kelas B. Kelas turunan bisa memiliki struktur

atau perilaku tambahan dari kelas supernya. Atau bahkan kelas turunan bisa mengubah

atau mengganti perilaku kelas supernya. Hubungan antara kelas turunan dan kelas super

sering dilukiskan dalam bentuk diagram di mana kelas turunan digambarkan di bawah

kelas supernya, dan dihubungkan dengan garis penghubung dengan tanda segitiga yang

diletakkan di dekat kelas supernya.

Page 136: Java With Eclipse

Dalam bahasa Java, ketika kita membuat suatu kelas, kita bisa menyatakan bahwa kelas

tersebut merupakan kelas turunan dari kelas lain. Jika kita buat kelas yang bernama "B"

dan kita ingin kelas ini menjadi kelas turunan dari kelas "A", kita akan tulis dalam bentuk :

class B extends A {

.

. // tambahan atau perubahan

. // struktur dan perilaku dari kelas A

.

}

Beberapa kelas dapat menurunkan kelas yang sama. Kelas-kelas turunan ini masing-

masing disebut kelas saudara, yaitu diwariskan dari satu kelas super yang sama. Struktur

dan perilaku kelas super ini akan dimiliki oleh masing-masing turunannya. Pada diagram

berikut, kelas B, C, dan D adalah kelas saudara. Pewarisan juga bisa dilakukan beberapa

kali, atau suatu kelas bisa memiliki cucu, buyut, dan seterusnya. Pada diagram, kelas E

merupakan kelas turunan kelas D, sehingga kelas E adalah "cucu" dari kelas A. Kelas E

masih bisa disebut turunan dari kelas A, walaupun bukan merupakan turunan langsungnya.

Page 137: Java With Eclipse

Mari kita buat sebuah contoh. Kita akan membuat program yang berhubungan dengan

kendaraan bermotor, yang meliputi mobil, truk, dan motor. Program tersebut memiliki

kelas yang dinamakan Kendaraan yang melambangkan semua jenis kendaraan bermotor.

Kelas Kendaraan memiliki variabel instansi seperti nomorPolisi dan pemilik dan

metode instansi yang bernama gantiPemilik. Variabel dan metode instansi ini bisa

digunakan oleh segala jenis kendaraan bermotor.

Ada 3 kelas turunannya yaitu Mobil, Truk dan Motor yang akan menyimpan variabel dan

metode khusus untuk setiap jenis kendaraan. Kelas Mobil misalnya memiliki variabel

jumlahPintu, kelas Truk memiliki variabel jumlahRoda, dan kelas Motor memiliki

variabel jumlahTak. Kelas-kelas ini bisa dideklarasikan dalam Java dalam bentuk

class Kendaraan {

int nomorPolisi;

Orang pemilik; // (anggap kelas Orang telah dibuat sebelumnya)

void gantiPemilik(Orang pemilikBaru) {

. . .

}

. . .

}

class Mobil extends Kendaraan {

Page 138: Java With Eclipse

int jumlahPintu;

. . .

}

class Truk extends Kendaraan {

int jumlahRoda;

. . .

}

class Motor extends Kendaraan {

int jumlahTak; // 2-tak atau 4-tak

. . .

}

Anggap mobilku adalah variabel dengan tipe Mobil akan dideklarasikan dan diinisialisasi

dengan pernyataan berikut

Mobil mobilku = new Mobil();

Dengan deklarasi seperti ini, maka program akan bisa mengakses mobilku.jumlahPintu,

karena jumlahPintu adalah variabel instansi dari kelas Mobil. Akan tetapi karena kelas

Mobil merupakan turunan dari kelas Kendaraan, maka mobil ini juga memiliki stuktur dan

perilaku dari kendaraan. Artinya program juga bisa mengakses mobilku.nomorPolisi,

mobilku.pemilik, dan menjalankan metode mobilku.gantiPemilik()

Dalam dunia nyata mobil, truk dan motor memang kendaraan (bukan hanya pada

program). Dalam arti objek yang memiliki tipe Mobil atau Truk atau Motor juga secara

otomatis objek bertipe Kendaraan. Fakta penting berikutnya :

Page 139: Java With Eclipse

Variabel yang dapat diisi referensi ke objek suatu kelas A juga dapat diisi referensi

ke objek kelas turunan dari kelas A.

Efek praktis dari penyataan ini adalah, objek dengan tipe Mobil dapat diisi ke dalam

variabel bertipe Kendaraan, atau dengan kata lain perintah berikut adalah valid

Kendaraan kendaraanku = mobilku;

atau bahkan juga perintah berikut

Kendaraan kendaraanku = new Mobil();

Setelah pernyataan di atas, variabel kendaraanku berisi referensi ke objek Kendaraan,

yang kebetulan merupakan instansi dari kelas turunannya, yaitu kelas Mobil. Objek akan

"mengingat" bahwa yang disimpan dalam variabel tersebut adalah objek bertipe Mobil,

bukan Kendaraan. Informasi tentang objek apa yang disimpan pada memori ikut

disertakan bersama objek tersebut, sehingga variabel yang bertipe Kendaraan akan tahu

dengan pasti tipe objek yang dirujuknya. Kita juga dapat menguji jenis objek yang

disimpan suatu variabel dengan menggunakan operator instanceof. Misalnya

if (kendaraanku instanceof Mobil) { ... }

menguji apakah objek yang dirujuk pada variabel kendaraanku merupakan objek bertipe

Mobil.

Kebalikannya, pernyataan berikut tidak bisa dilakukan

mobilku = kendaraanku;

karena kendaraanku bisa bertipe objek lain seperti Truk atau Motor. Apabila kita tahu

persis bahwa kendaraanku bertipe Mobil, kita bisa menggunakan casting, untuk memberi

tahu komputer untuk memperlakukan variabel kendaraanku memiliki tipe Mobil. Jadi kita

bisa gunakan perintah

mobilku = (Mobil)kendaraanku;

Atau kita juga bisa mengakses ((Mobil)kendaraanku).jumlahPintu. Mari kita gunakan

kelas ini dalam program, dan kita ingin mencetak informasi yang sesuai dengan suatu

kendaraan. Misalnya:

System.out.println("Data Kendaraan:");

System.out.println("Nomor polisi: " + kendaraanku.nomorPolisi);

if (kendaraanku instanceof Mobil)

System.out.println("Jenis kendaraan: Mobil");

Mobil m = (Mobil)kendaraanku;

System.out.println("Jumlah pintu: " + m.jumlahPintu);

}

else if (kendaraanku instanceof Truk) {

System.out.println("Jenis kendaraan: Truk");

Page 140: Java With Eclipse

Truk t = (Truk)kendaraanku ;

System.out.println("Jumlah roda: " + t.jumlahRoda);

}

else if (kendaraanku instanceof Motor) {

System.out.println("Jenis kendaraan: Motor");

Motor sm = (Motor)kendaraanku ;

System.out.println("Jumlah tak: " + sm.jumlahTak);

}

Lihat bahwa untuk setiap jenis objek, komputer akan menguji satu per satu tipe objek yang

disimpan dalam kendaraanku. Jika kendaraanku[code] merujuk pada objek bertipe

Truk maka casting [code](Mobil)kendaraanku akan menampilkan pesan kesalahan.

Contoh lain, mari kita buat program untuk menggambar suatu bentuk geometri pada layar.

Misalnya bentuk geometri tersebut terdiri dari persegi panjang, oval, dan kotak bersudut

lingkar dengan berbagai warna.

Kelas yang akan kita buat adalah PersegiPanjang, Oval, dan KotakLingkar. Ketiga kelas

tersebut memiliki kelas super yang sama yang disebut BentukGeometris. Kelas

BentukGeometris memiliki variabel instansi warna, lokasi, dan ukuran. Untuk mengganti

warna kita bisa mengganti variabel instansi warna pada kelas ini, kemudian menjalankan

metode instansi gambar() untuk menggambar bentuk tersebut dengan warna baru:

class BentukGeometris {

Color warna; // Warna suatu bentuk geometri

// (Kelas Color diimport dari paket java.awt)

void setWarna(Color warnaBaru) {

// Metode untuk mengganti warna

warna = warnaBaru; // ganti nilai variabel instansi

gambar(); // gambar ulang bentuk geometris ini, dengan warna baru

}

Page 141: Java With Eclipse

void gambar() {

// metode untuk menggambar

? ? ? // perintaha apa yang harus diletakkan di sini?

}

. . . // variabel dan metode instansi lain

} // akhir kelas BentukGeometris

Sekarang metode gambar() mungkin menjadi serba salah. Masalahnya, setiap bentuk

digambar dengan cara berbeda. Metode setWarna() dapat digunakan oleh semua bentuk.

Lalu bagaimana komputer tahu bagaimana menggambar bentuk tersebut jika metode

gambar() dipanggil? Mudahnya, kita bisa jawab dengan : Komputer akan menjalankan

perintah gambar() dengan meminta bentuk tersebut untuk menggambar sendiri. Setiap

objek bentuk tahu apa yang harus dilakukan untuk menggambar dirinya.

Dalam prakteknya, ini berarti setiap kelas turunan dari kelas BentukGeometris memiliki

metode gambar() sendiri-sendiri, yaitu :

class PersegiPanjang extends BentukGeometris {

void gambar() {

. . . // perintah untuk menggambar persegi panjang

}

. . . // metode atau variabel lain

}

class Oval extends BentukGeometris {

void gambar() {

. . . // perintah untuk menggambar oval

}

. . . // metode atau variabel lain

}

class KotakLingkar extends BentukGeometris {

void gambar() {

. . . // perintah untuk menggambar kotak bersudut lingkar

}

. . . // metode atau variabel lain

}

Jika gambarku adalah variabel bertipe BentukGeometri, variabel ini bisa merujuk pada

objek dengan bertipe PersegiPanjang, Oval, atau KotakLingkar. Ketika program

Page 142: Java With Eclipse

dijalankan, isi variabel gambarku akan berubah-ubah, tergantung pada objek yang

dirujuknya. Suatu saat di tengah program, jika perintah gambarku.gambar() dijalankan,

maka metode gambar() akan dijalankan tergantung pada isi variabel gambarku saat itu.

Kita tidak akan bisa menebak metode apa yang akan dipanggil pada suatu saat hanya

dengan membaca program tersebut, karena kita tidak pernah tahu isi variabel gambarku

pada suatu saat tertentu. Misalnya perintah gambar() berada dalam suatu perulangan yang

dijalankan berkali-kali. Maka akan sangat mungkin perintah gambarku.gambar()

dipanggil berulang-ulang tetapi dengan objek yang berbeda-beda.

Kita sebut metode gambar() bersifat polimorfis. Suatu metode disebut polimorfis jika aksi

yang dilakukan oleh suatu metode berbeda-beda tergantung pada objek aktual pada saat

metode itu dijalankan. Polimorfisme adalah fitur utama dalam pemrograman berorientasi

objek.

Mungkin akan lebih mudah dimengerti jika kita ganti bahasanya : Dalam PBO, memanggil

metode sering disebut juga dengan mengirim pesan kepada suatu objek. Objek tersebut

merespon pesan tersebut dengan melaksanakan metode yang sesuai. Pernyataan

"gambarku.gambar();" adalah pesan yang dikirim ke objek gambarku. Karena objek

tersebut tahu jenis objeknya sendiri, dia akan tahu bagaimana merespon pesan tersebut.

Dari sudut pandang ini, komputer akan selalu mengeksekusi perintah

"gambarku.gambar();" dengan cara yang sama, yaitu dengan mengirim pesan. Pesan

tersebut bergantung pada siapa yang menerima pesan tersebut.

Dengan kata lain, objek merupakan sesuatu yang memiliki perilaku aktif, yaitu sesuatu

yang bisa mengirim dan menerima pesan. Polimorfisme dianggap sebagai sifat yang

natural jika dilihat dari sudut pandang ini. Polimorfisme juga berarti bahwa beberapa objek

dapat merespon suatu pesan dengan cara yang berbeda-beda.

Salah satu keindahan dari poliformisme adalah kita bisa membuat kode program tanpa

harus mengetahui persis apa yang akan dilakukan program saat kita menulis program

tersebut. Jika kita ingin menambah objek lain, misalnya segitiga, maka kita cukup menulis

kelas turunan baru dan membuat metode gambar() sendiri. Secara otomatis, program akan

tahu jika kita beri perintah "gambarku.gambar()" maka metode gambar() pada kelas

segitiga akan dijalankan apabila gambarku menunjuk pada objek yang memiliki kelas

segitiga.

Page 143: Java With Eclipse

Ketika suatu objek, misalnya PersegiPanjang, Oval, atau KotakLingkar, harus

menggambar dirinya sendiri, metode gambar() yang sesuai dengan objek yang menerima

pesan akan dilaksanakan. Pertanyaannya, apa yang harus kita isi pada metode gambar() di

kelas BentukGeometri?

Jawabannya: kosongkan saja. Intinya kelas BentukGeometri adalah kelas abstrak, karena

tidak ada cara untuk menggambar BentukGeometri. Apabila kelas tersebut telah

direalisasikan dalam suatu bentuk, misalnya PersegiPanjang, barulah objek tersebut bisa

menggambar sesuatu.

Lalu kenapa kita harus mendeklarasikan metode gambar() di kelas BentukGeometri?

Jawabannya, itu harus ada karena metode ini dibutuhkan untuk memanggil metode

setWarna() pada kelas BentukGeometri. Program kita akan menampilkan pesan

kesalahan jika kita berikan perintah gambarku.gambar(), karena gambarku bertipe

BentukGeometri, apabila di dalam kelas ini tidak ada metode yang bernama gambar().

Kita bisa menyebut kelas BentukGeometri merupakan kelas abstrak. Kelas abstrak adalah

kelas yang tidak bisa digunakan untuk membuat suatu objek, dan hanya digunakan untuk

membuat kelas turunan. Kelas abstrak hanya ada untuk mengekspresikan sifat umum yang

dimiliki oleh kelas-kelas turunannya.

Demikian juga, kita bisa menyebut metode gambar() pada kelas BentukGeometri

merupakan metode abstrak, karena metode ini bukan untuk dipanggil. Akan tetapi metode

ini ada untuk memberi tahu komputer bahwa semua kelas turunannya mengerti dan bisa

menjalankan metode gambar().

BentukGeometri dan metode gambar() secara sematik merupakan kelas dan metode

abstrak. Kita juga bisa memberi tahu komputer secara langsung dengan memberi sifat

"abstract" pada definisinya. Untuk metode abstrak, blok perintahnya diganti dengan titik

koma (;). Metode ini harus diimplementasikan secara detail pada kelas turunannya.

Perhatikan contoh berikut.

abstract class BentukGeometri {

Color warna;

void setWarna(Color warnaBaru) {

// metode untuk mengganti warna suatu bentuk

warna = warnaBaru; // ganti isi variabel instansi warna

gambar(); // menggambar kembali suatu bentuk dengan warna baru

}

Page 144: Java With Eclipse

abstract void gambar();

// metode abstrak yang harus diimplementasikan

// pada kelas turunannya

. . . // variabel dan metode instansi lainnya

} // akhir kelas BentukGeometri

Setelah kita buat seperti ini, kita tidak bisa lagi membuat objek langsung dari kelas

BentukGeometri.

Dalam Java, setiap kelas yang kita buat akan memiliki kelas super, atau dengan kata lain

setiap kelas merupakan turunan dari kelas lain. Jika kita tidak memberi kelas supernya

(melalui operator extends), maka kelas tersebut otomatis memiliki kelas super Object,

yaitu kelas bawaan yang sudah didefinisikan dalam paket java.lang. Kelas Object adalah

satu-satunya kelas yang tidak memiliki kelas super.

Jadi dengan demikian, perintah

class Kelasku { ... }

sama artinya dengan

class Kelasku extends Object { . . . }

Semua kelas akan merupakan turunan langsung atau tidak langsung dari kelas Object.

Artinya suatu obek yang merupakan kelas apapun dapat direferensikan oleh variabel

bertipe Object. Kelas Objek memiliki sifat-sifat umum yang dapat digunakan oleh semua

objek. Kelas Object adalah kelas yang paling abstrak dari kelas-kelas lainnya.

Kelas Object digunakan dalam beberapa kasus di mana kumpulan objek yang sangat

umum ingin diolah. Misalnya, Java memiliki kelas standar java.util.ArrayList yang

merupakan kumpulan Objects. ArrayList akan dibahas kemudian dalam topik tentang

struktur data dan algoritma. Kelas ini digunakan untuk menampung kumpulan objek, tak

ditentukan jumlahnya, dan bisa diperbanyak ketika objek baru ditambahkan. Objek yang

dapat ditampung pada dasarnya adalah semua objek dari beragam kelas.

Kita dapat juga membuat program untuk menampung semua BentukGeometri yang telah

digambar di layar dalam kontainer ArrayList. Milsanya ArrayList kita bernama

kumpulanGambar. Suatu objek dengan tipe BentukGeometri misalnya gambarku dapat

ditambahkan ke dalam kumpulan ini dengan menggunakan perintah

Page 145: Java With Eclipse

"kumpulanGambar.add(gambarku);". Gambar tersebut dapat dibuang dari dalam

kumpulan dengan perintah "kumpulanGambar.remove(gambarku);". Jumlah obejk dalam

kumpulanGambar dapat diubah dengan perintah "kumpulanGambar.size()". Juga kita bisa

mengambil gambar ke-n dari dalam kumpulanGambar dengan perintah

"kumpulanGambar.get(n);". Perlu diingat bahwa metode tersebut akan mengembalikan

objek bertipe Object bukan BentukGeometri, dan sebetulnya kontainer ini bisa

menampung objek apa saja, bukan hanya BentukGeometri, sehingga untuk mengambil

objek ke-n yang kemudian kita letakkan dalam variabel bertipe BentukGeometri, kita bisa

gunakan perintah

gambarku = (BentukGeometri)kumpulanGambar.get(n);

Katakan misalnya kita ingin menggambar semua objek dengan berbagai tipe di dalam

kumpulan tersebut, kita bisa gunakan perulangan for sederhana (sekaligus memberi contoh

betapa indagnya PBO dan polimorfisme), yaitu dengan :

for (int n = 0; n < kumpulanGambar.size(); n++) {

BentukGeometri bg = (BentukGeometri)kumpulanGambar.get(n);

bg.gambar();

}

Penutup

Dalam pemrograman sehari-hari, terutama bagi programmer yang baru belajar dan bekerja

dengan objek, penurunan kelas akan sering digunakan. Salah satunya adalah untuk

memperluas kegunaan suatu kelas, yang disesuaikan dengan situasi dan kondisi

permasalahan yang kita hadapi. Kita bisa membuat kelas baru yang merupakan turunan

kelas yang sudah ada, menambah beberapa variabel dan metode instansi baru, yaitu dengan

operator extends seperti dijelaskan sebelumnya pada bagian ini.

Secara umum, sintaksnya adalah dalam bentuk

class kelas_turunan extends kelas_yang_sudah_ada {

... // tambahan atau perubahan variabel dan metode instansi

}

Kita akan lihat nanti bahwa banyak kelas-kelas standar pada Java yang digunakan hanya

sebagai kelas dasar yang untuk dikembangkan lebih jauh oleh kita sebagai programmer

this dan super

Meskipun ide dasar dari pemrograman berorientasi objek merupakan konsep yang cukup

sederhana dan jelas, akan tetapi untuk memahaminya dengan baik kita membuthkan lebih

banyak waktu. Sayangnya, banyak sekali detail yang harus diulas di luar konsep dasarnya.

Bagian ini dan bagian berikutnya akan berbicara tentang hal-hal detail, yang tidak perlu

Page 146: Java With Eclipse

dikuasai dalam waktu singkat, akan tetapi pengenalan tentang hal detail ini sangat berguna

paling tidak sebagai bahan referensi.

Variabel spesial this dan super

Anggota statik suatu kelas bisa digunakan langsung di dalam kelas tersebut. Untuk

digunakan di kelas lain, maka ia harus dipanggil dengan nama lengkapnya dalam bentuk

"namaKelas.namaAnggota", misalnya "System.out" adalah variabel anggota statik

dengan nama "out" di dalam kelas "System". Kita bisa menggunakan nama lengkap kelas

tersebut di manapun, bahkan dari dalam kelas yang sama, misalnya karena nama anggota

tersebut disembunyikan oleh variabel lokal yang namanya sama.

Variabel dan metode instansi juga bisa digunakan langsung di dalam kelas tersebut,

misalnya suatu variabel instansi bisa digunakan langsung oleh metode instansi di kelas

yang sama. Anggota instansi juga memiliki nama lengkap akan tetapi ingat bahwa anggota

instansi dimiliki oleh objek, bukan kelas. Sehingga untuk memanggil suatu anggota

instansi, kita harus menggunakan nama objek yang diciptakan dari kelas tersebut, dalam

bentuk namaObjek.namaAnggota.

Akan tetapi, misalnya kita sedang menulis metode instansi di suatu kelas. Bagaimana

caranya kita memanggil anggota instansi kelas yang sama jika kita belum tahu nama objek

apa yang diciptakan dari kelas ini?

Java memiliki variabel spesial yang dinamakan "this" yang bisa digunakan untuk kasus di

atas. Variabel ini digunakan pada suatu objek untuk memanggil metode atau variabel

instansi pada objek yang sama. Maksudnya, this, merujuk pada "objek ini" yaitu objek di

mana metode sedang dijalankan. Jika x adalah variabel instansi dalam suatu objek, maka

this.x adalah nama lengkap variabel tersebut jika dipanggil dari dalam objek yang sama.

Jika metode suatuMetode() adalah metode instansi pada suatu objek, maka

this.suatuMetode() bisa digunakan untuk memanggil metode tersebut dari objek yang

sama. Ketika komputer menjalankan perintah tersebut, komputer akan mengganti variabel

this dengan objek yang berjalan pada saat itu.

Salah satu kegunaaan this adalah pada konstruktor, misalnya :

public class Murid {

private String nama; // Nama murid

public Murid(String nama) {

// Konstruktor, membuat murid dengan nama yang diberikan

this.nama = nama;

}

Page 147: Java With Eclipse

.

. // Variabel dan metode lain.

.

}

Dalam konstruktor di atas, variabel instansi nama disembunyikan oleh parameter formal

yang bernama sama. Akan tetapi, variabel instansi masih tetap bisa dipanggil dengan nama

lengkapnya, this.nama. Cara ini merupakan cara yang lazim, artinya kita tidak perlu

mengganti nama parameter formal dengan nama lain agar namanya tidak bentrok dengan

nama salah satu variabel instansinya. Kita bisa menggunakan nama parameter formal yang

sama persis dengan variabel instansinya.

Ada lagi kegunaan lain dari this. Ketika kita sedang menulis metode instansi, kita ingin

memberikan objek sebagai parameter aktual. Dalam hal ini, kita bisa menggunakan this

sebagai parameter aktualnya. Misalnya, kita ingin mencetak suatu objek sebagai string, kita

bisa menggunakan perintah "System.out.println(this);". Atau kita ingin mengisi nilai

this ke dalam variabel lain. Pokoknya, kita bisa melakukan segala hal seperti layaknya

variabel kecuali mengganti isinya.

Java juga memiliki variabel spesial yang dinamakan "super" untuk digunakan dalam

metode instansi. Variabel super digunakan dalam kelas turunan. super mengacu pada

objek di mana metode tersebut berada, akan tetapi ia merupakan bagian dari kelas super

dari objek tersebut.

Suatu kelas bisa ditambah atau dimodifikasi dari kelas turunannya. Variabel super hanya

mengacu pada bagian objek sebelum ia ditambah atau dimodifikasi, atau dengan kata lain

bentuk aslinya sebelum dia diturunkan, yang dalam hal ini sama dengan kelas supernya.

Misalnya kita akan menulis suatu kelas, dan kelas tersebut memiliki metode instansi

bernama suatuMetode(). Pernyataan super.suatuMetode() berarti menjalankan

suatuMetode() pada kelas supernya. Jika tidak ada metode sutuMetode() pada kelas

supernya, Java akan menampilkan pesan kesalahan sintaks.

Alasan mengapa Java memiliki variabel super adalah karena banyak hal yang mungkin

tersembunyi pada kelas turunan. Misalnya karena kita mengimplementasikan fungsi lain

pada kelas turunan dengan nama yang sama (dalam kaitannya dengan polimorfisme,

misalkan).

Ketika kita membuat metode baru pada kelas turunan yang memiliki nama dan jenis

parameter yang sama dengan metode pada kelas supernya, metode dari kelas super akan

Page 148: Java With Eclipse

disembunyukan. Dalam bahasa pemrograman, metode ini menimpa (override) metode dari

kelas supernya. Variabel super bisa digunakan untuk mengakses metode aslinya yang

didefinisikan di kelas supernya.

Kegunaan utama dari super biasanya untuk memperluas kegunaan metode yang sudah

ada, bukan menggantinya secara keseluuruhan. Metode baru bisa menggunakan super

untuk menjalankan instruksi pada kelas supernya, kemudian menambahkan instruksi lain

pada kelas turunannya.

Misalnya, kita memiliki kelas PasanganDadu yang memiliki metode kocok. Kemudian kita

akan membuat kelas turunan yaitu DaduGrafis yang berfungsi untuk menggambar dadu

pada layar. Metode kocok() dalam DaduGrafis harus melakukan apa yang dilakukan oleh

PasanganDadu, dengan tambahan perintah untuk menggambar dadu tersebut di layar. Kita

bisa tulis definisi kelas DaduGrafis dalam bentuk :

public class DaduGrafis extends PasanganDadu {

public void kocok() {

// Mengocok dadu, kemudian menggambarnya di layar

super.kocok(); // Panggil metode kocok() di kelas PasanganDadu

gambar(); // Gambar ulang dadu

}

.

. // Metode dan variabel lain, termasuk metode gambar()

.

}

Dengan cara ini kita bisa memperluas apa yang dilakukan oleh metode kocok() pada kelas

supernya tanpa mengetahui dengan detail apa yang dilakukan langkah per langkah di kelas

supernya.

Konstuktor pada Kelas Turunan

Konstruktor tidak bisa diturunkan, artinya jika kita membuat kelas turunan dari suatu kelas,

konstruktor pada kelas supernya tidak termasuk bagian yang diturunkan. Jika kita ingin

konstruktor tersebut pada kelas turunannya, maka kita harus membuat kembali konstruktor

tersebut di kelas yang baru. Jika tidak, maka Java akan membuat konstruktor standar tanpa

parameter apa-apa.

Hal ini mungkin menjadi masalah jika konstruktor pada kelas supernya melakukan banyak

tugas. Artinya kita harus mengulang kembali menuliskan semua instruksi pada kelas

supernya di kelas turunan yang baru. Masalahnya akan lebih rumit jika kita tidak

Page 149: Java With Eclipse

mengetahui sama sekali apa yang dilakukan kelas supernya, misalnya apabila tidak ada

kode sumbernya.

Cara yang paling mudah adalah menggunakan super. Di baris pertama konstruktor baru

kita di kelas turunannya, kita bisa menggunakan super untuk memanggil konstruktor kelas

supernya. Sintaksnya sedikit aneh dan membingungkan, dan hanya bisa digunakan untuk

satu situasi saja : Sintaksnya mirip seperti memanggil subrutin super (meskipun

sebenarnya super bukan subrutin dan kita tidak bisa memanggil konstruktor seperti kita

memanggil subrutin biasa).

Misalnya kelas PasanganDadu memiliki konstruktor yang memiliki dua parameter bertipe

int. Maka kita bisa membuat konstruktor pada kelas DaduGrafis seperti :

public class DaduGrafis extends PasanganDadu {

public DaduGrafis() { // Konstruktor kelas ini

super(3,4); // Panggil konstruktor dari kelas PasanganDadu

// dengan parameter 3 dan 4

inisialisasiGrafis(); // Lakukan inisialisasi grafis

// untuk digunakan pada kelas DaduGrafis

}

.

. // Konstruktor, variabel dan metode instansi lain

.

}

Antar Muka (interface), Kelas Bertingkat, dan Detail Lain

Antar Muka

Beberapa bahasa pemrograman berorientasi objek, misalnya C++, membolehkan suatu

kelas memiliki dua atau lebih kelas super. Hal ini disebut pewarisan ganda (multiple

inheritance). Pada ilustrasi berikut, kelas E memiliki kelas super A dan B, sedangkan kelas

F memiliki 3 kelas super.

Page 150: Java With Eclipse

Pewarisan ganda seperti ini tidak diperbolehkan pada Java. Desainer Java ingin menjaga

agar bahasa Java tetap sederhana, dan mereka merasa pewarisan ganda ini sangat kompleks

dengan keuntungan yang tidak begitu besar. Akan tetapi, Java memiliki fitur lain yang bisa

digunakan seperti halnya pewarisan berganda, yaitu antar muka (interface).

Kita telah mengenal istilah "antar muka" sebelumnya, yaitu dalam konteks umum tentang

kotak hitam dan subrutin. Antar muka suatu subrutin terdiri dari nama, jenis keluarannya,

jumlah dan tipe parameternya. Informasi ini dibutuhkan jika kita ingin memanggi subrutin

tersebut. Suatu subrutin juga memiliki implementasi : yaitu blok yang berisi perintah yang

akan dijalankan ketika subrutin ini dipanggil.

Dalam Java, kata interface adalah kata kunci yang memiliki arti tambahan. Suatu

interface dalam hal ini adalah antar muka yang terdiri dari subrutin tanpa implementasi

apa-apa. Suatu kelas dapat mengimplementasi suatu interface dengan memberikan kode

detail pada setiap subrutin yang ditulis pada interface tersebut. Berikut adalah contoh

interface Java sederhana :

public interface Gambar {

public void gambar(Graphics g);

}

Deklarasi di atas mirip dengan definisi suatu kelas, akan tetapi isi metode gambar()

dikosongkan. Suatu kelas yang mengimplementasi interface ini, yaitu

interfaceGambar, harus mengisi implementasi metode gambar() ini. Tentunya kelas

tersebut juga bisa memiliki variabel dan metode lain. Misalnya,

class Garis implements Gambar {

public void gambar(Graphics g) {

. . . // perintah untuk menggambar garis

}

. . . // variabel dan metode lain

}

Kelas apapun yang mengimplementasi antar muka Gambar[code] harus memberikan

detail apa yang akan dilakukan oleh metode [code]gambar(). Objek yang

diciptakan dari kelas tersebut akan memiliki metode gambar(). Perlu diingat bahwa hanya

menambah metode gambar() saja tidak cukup. Definisi kelas yang ingin

mengimplementasikan suatu interface harus menulis "implements Gambar" dalam

definisi kelasnya.

Suatu kelas bisa menurunkan hanya satu kelas lain, akan tetapi suatu kelas bisa

mengimplementasikan lebih dari suatu antar muka. Sebenarnya, suatu kelas bisa

Page 151: Java With Eclipse

menurunkan kelas lain dan mengimplementasikan satu atau lebih antar muka sekaligus.

Misalnya

class LingkaranBerwarna extends Lingkaran

implements Gambar, BerisiWarna {

. . .

}

Intinya adalah meskipun interface bukan kelas, akan tetapi interface mirip dengan

kelas. suatu interface mirip seperti kelas abstrak, yaitu kelas yang hanya digunakan

untuk membuat kelas lain, bukan untuk membuat objek. Subrutin pada suatu interface

merupakan metode abstrak yang harus diimplementasikan pada kelas kongkrit yang

mengimplementasikan interface tersebut.

Seperti kelas abstrak, meskipun kita tidak bisa membuat objek dari interface, akan tetapi

suatu variabel dapat bertipe suatu interface. Misalnya, jika Gambar adalah suatu

interface, dan jika Garis dan LingkaranBerwarna adalah kelas yang

mengimplementasikan Gambar, maka kita bisa menulis kode seperti :

Gambar gambarku; // Deklarasi variabel dengan tipe Gambar.

// Variabel ini bisa diisi objek yang

// mengimplementasi interface Gambar

gambarku = new Garis(); // gambarku berisi objek dengan kelas Garis

gambarku.gambar(g); // memanggil metode gambar() dari kelas Garis

gambarku = new LingkaranBerwarna(); // Sekarang gambarku berisi objek

dengan

// kelas LingkaranBerwarna

gambarku.gambar(g); // memanggil metode gambar() dari kelas

LingkaranBerwarna

Variabel dengan tipe Gambar boleh merujuk pada kelas apapun yang

mengimplementasikan antar muka Gambar. Pernyataan di atas seperti

"gambarku.gambar(g)" boleh ditulis karena gambarku adalah variabel dengan tipe

Gambar, dan setiap objek bertipe Gambar pasti memiliki metode gambar().

Catatan bahwa tipe data merupakan sesuatu yang biasa digunakan untuk mendeklarasikan

variabel. Tipe data juga digunakan untuk memberikan tipe suatu parameter pada subrutin,

atau sebagai tipe keluaran suatu fungsi. Pada Java, tipe data bisa berupa kelas, interface,

atau salah satu dari 8 tipe data primitif. Dari semuanya, hanya kelas yang bisa digunakan

untuk membuat objek baru.

Page 152: Java With Eclipse

Kita biasanya tidak perlu menulis interface kita sendiri hingga program kita menjadi

sangat kompleks. Akan tetapi ada beberapa interface yang sudah disediakan oleh Java

yang mungkin bisa digunakan atau diimplementasi dalam program kita.

Kelas Bertingkat

Suatu kelas merupakan blok bangunan suatu program, yang melambangkan suatu ide

beserta data dan perilaku yang dimilikinya. Kadang-kadang kita mungkin berasa sedikit

aneh untuk membuat kelas kecil hanya untuk menggabungkan beberapa data. Akan tetapi

kadang-kadang kelas-kelas kecil ini sering bermanfaat dan penting. Untungnya Java

membolehkan kita untuk membuat kelas di dalam kelas lain, sehingga kelas-kelas kecil ini

tidak perlu berdiri sendiri. Kelas kecil ini menjadi bagian dari suatu kelas besar yang bisa

melakukan hal-hal kompleks lainnya. Kelas kecil ini misalnya berguna untuk mendukung

operasi yang akan dikerjakan oleh kelas besarnya.

Dalam Java, kelas bertingkat atau kelas bagian dalam adalah kelas yang ditulis di dalam

definisi kelas lain. Kelas bagian dalam ini bisa memiliki nama atau anonim (tanpa nama).

Kelas bagian dalam yang memiliki nama tampak seperti kelas biasa, tetapi ia ditulis di

dalam kelas lain. (Kelas bagian dalam ini juga bisa memiliki kelas bagian dalam yang lain,

akan tetapi ingat akan konsekuensi kerumitannya apabila kita membuat terlalu banyak

tingkatan).

Seperti komponen lain dalam suatu kelas, kelas bagian dalam yang memiliki nama bisa

berupa kelas statik atau kelas non-statik. Kelas bertingkat statik merupakan bagian dari

struktur statik dari kelas yang menaunginya. Kelas tersebut bisa digunakan di dalam kelas

induknya untuk membuat objek seperti biasa. Jika tidak dideklarasikan sebagai private,

makan kelas tersebut juga bisa digunakan dari luar kelas induknya. Jika digunakan dari

luar kelas induknya, namanya harus jelas mencantumkan nama kelas induknya. Mirip

seperti komponen statik dari suatu kelas : kelas bertingkat statik adalah bagian kelas di

mana kelas tersebut mirip dengan variabel anggota statik lainnya di dalam kelas tersebut.

Misalnya, suatu kelas bernama ModelRangkaKawat melambangkan kumpulan garis dalam

ruang 3 dimensi. Misalnya kelas ModelRangkaKawat memiliki kelas bertingkat statik yang

bernama Garis yaitu sebuah garis. Maka dari luar kelas ModelRangkaKawat, kelas Garis

akan dipanggil sebagai ModelRangkaKawat.Garis.

Kelas ModelRangkaKawat dan kelas bagian dalamnya dapat dituliskan seperti berikut :

public class ModelRangkaKawat {

Page 153: Java With Eclipse

. . . // anggota lain kelas ModelRangkaKawat

static public class Garis {

// Melambangkan garis dari titik (x1,y1,z1)

// ke titik (x2,y2,z2) dalam ruang 3-dimensi

double x1, y1, z1;

double x2, y2, z2;

} // akhir kelas Garis

. . . // anggota lain kelas ModelRangkaKawat

} // akhir kelas ModelRangkaKawat

Di dalam kelas ModelRangkaKawat, objek Garis bisa dibuat dengan konstruktor "new

Garis()". Di luar kelas, perintah "new ModelRangkaKawat.Garis()" harus digunakan.

Kelas bertingkat statik memiliki akses penuh kepada anggota dari kelas induknya,

termasuk ke anggota private. Mungkin ini juga motivasi sebagian orang untuk membuat

kelas bertingkat, karena kelas bagian dalamnya bisa mengakses anggota private kelas lain

tanpa harus membuat variabel atau metode anggotanya menjadi public.

Ketika kita mengkompilasi definisi kelas di atas, dua file kelas akan dibuat. Meskipun

definisi kelas Garis berada di dalam ModelRangkaKawat, akan tetapi kelas Garis akan

disimpan dalam file terpisah. Nama file kelas Garis akan menjadi

ModelRangkaKawat$Garis.class

Kelas bertingkat yang tidak statik, pada prakteknya, tidak jauh berbeda dengan kelas

bertingkat statik, akan tetapi kelas bertingkat non-statik berkaitan dengan suatu objek,

bukan kelas induknya.

Anggota non-statik dari suatu kelas sebenarnya bukan merupakan bagian dari kelas itu. Hal

ini juga berlaku untuk kelas bertingkat non-statik seperti juga bagian kelas non-statik

lainnya. Anggota non-statik suatu kelas menjelaskan apa yang akan disimpan dalam objek

yang diciptakan dari kelas tersebut. Hal ini juga berlaku (secara logis) dari kelas bertingkat

non-statik.

Dengan kata lain, setiap objek yang diciptakan dari kelas induknya memiliki kopi kelas

bertingkat masing-masing. Kopi ini memiliki akses ke semua variabel dan metode instansi

objek tersebut. Dua objek kelas bagian dalam pada dua objek induk merupakan objek

berbeda karena metode dan variabel instansi yang bisa diakses berasal dari objek yang

berbeda.

Page 154: Java With Eclipse

Pada dasarnya, aturan untuk menentukan kapan suatu kelas bisa dimasukkan ke dalam

kelas lain sebagai kelas statik atau non-statik adalah : Jika kelas tersebut perlu

menggunakan variabel atau metode instansi suatu objek (bukan variabel atau metode statik

kelas), maka kelas tersebut harus dibuat non-statik, jika tidak maka harus dibuat statik.

Dari luar kelas induknya, kelas bertingkat non-statik harus dipanggil dalam bentuk

namaVariabel.NamaKelasBertingkat, misalnya namaVariabel adalah variabel yang

merujuk pada objek yang memiliki kelas bertingkat tersebut. Sebetulnya cara ini agak

langka. Kelas bertingkat non-statik biasanya digunakan hanya di dalam kelas induknya,

sehingga bisa diakses dengan nama yang sederhana.

UNtuk membuat objek yang merupakan kelas bertingkat non-statik, kita harus membuat

objek yang merupakan kelas induknya. (Ketika bekerja di dalam kelas, objek "this" akan

secara otomatis digunakan). Objek dari kelas bertingkat tersebut dihubungkan secara

permanen dengan objek dari kelas induknya, dan memiliki akses penuh atas anggota kelas

induknya.

Mari lihat contoh berikut, dan mungkin bisa memberi pemahaman lebih baik bagaimana

kelas bertingkat non-statik sebetulnya merupakan hal yang sangat alami. Misalnya suatu

kelas yang melambangkan permainan kartu. Kelas ini memiliki kelas beringkat yang

melambangkan para pemainnya. Struktur MainKartu bisa berbentuk seperti :

class MainKartu { // Melambangkan permainan kartu

class Pemain { // Melambangkan salah satu pemain game ini

.

.

.

} // akhir kelas Pemain

private Tumpukan tumpukan; // Tumpukan kartu

.

.

.

} // akhir kelas MainKartu

Jika game adalah variabel dengan tipe MainKartu, maka game memiliki kelas

Pemain[code] sendiri. Dalam metode instansi objek [code]MainKartu, objek

Pemain bisa dibuat dengan perintah "new Pemain()", seperti halnya kelas biasa. (Objek

Pemain bisa dibuat di luar kelas MainKartu dengan perintah seperti "new

Page 155: Java With Eclipse

game.Pemain()", tapi ini jarang dilakukan). Objek Pemain memiliki akses ke variabel

instansi tumpukan dalam objek MainKartu.

Masing-masing objek MainKartu memiliki tumpukan dan Pemain sendiri-sendiri. Pemain

kartu pada game tersebut akan menggunakan tumpukan kartunya sendiri sedangkan

pemain kartu pada game yang lain akan menggunakan tumpukan kartu lain lagi.

Jika Pemain merupakan kelas bertingkat statik, maka pemain tersebut akan bermain di

semua permainan kartu, yang tentu saja tidak mungkin terjadi.

Dalam beberapa kasus, mungkin kita harus menulis kelas bertingkat dan kemudian

menggunakan kelas tersebut hanya 1 kali dalam program kita. Apakah berguna membuat

kelas bertingkat jika begini kondisinya? Mungkin ya mungkin tidak. Dalam kasus seperti

ini kita juga bisa membuat kelas bertingkat anonim. Kelas anonim dapat dibuat dengan

menggunakan variasi dari operator new dengan bentuk

new kelassuper_atau_interface () {

metode_dan_variabel

}

Konstruktor ini membuat suatu kelas baru tanpa memberi nama, dan pada saat yang sama

membuat objek dari kelas tersebut. Bentuk operator [code] seperti ini bisa digunakan

dalam pernyataan apapun di mana pernyataan new biasa digunakan. Maksud dari

pernyataan di atas adalah untuk membuat : "objek baru di dalam suatu kelas yang namanya

sama dengan kelassuper_atau_interface dengan ditambah dengan

metode_dan_varaibel baru."

Artinya pernyataan di atas sama dengan membuat objek baru dengan konfigurasi yang baru

pula. Kita juga bisa membuat kelas anonim yang diturunkan dari interface. Dalam hal

ini, kelas anonim tersebut harus mengimplementasikan semua metode yang dideklarasikan

oleh interface tersebut.

Kelas anonim sering digunakan untuk menangani event pada GUI (graphical user

interfaces). Misalnya interface Gambar seperti didefinisikan di awal bagian ini. Misalnya

kita ingin membuat objek berupa gambar bujur sangkar berisi warna merah dengan ukuran

100 x 100 piksel. Daripada membuat kelas baru kemudian menggunakan kelas tersebut

untuk membuat objek, kita bisa menggunakan kelas anonim untuk membuat objek

sekaligus dalam satu pernyataan :

Gambar kotakMerah = new Gambar() {

void gambar(Graphics g) {

Page 156: Java With Eclipse

g.setColor(Color.red);

g.fillRect(10,10,100,100);

}

};

Tanda titik koma (;) di akhir pernyataan ini bukan bagian dari definisi suatu kelas, tapi

merupakan bagian dari pernyataan secara keseluruhan.

Ketika kelas Java dikompilasi, setiap kelas bertingkat anonim akan dibuat dalam file kelas

terpisah. Jika nama kelas utama adalah KelasUtama, misalnya, maka nama file kelas untuk

setiap kelas bertingkat anonimnya menjadi KelasUtama$1.class, KelasUtama$2.class,

KelasUtama$3.class dan seterusnya.

Sifat Akses dalam Kelas

Suatu kelas dapat dideklarasikan sebagai public, yang bisa diakses dari manapun.

Beberapa kelas harus dideklarasikan sebagai publik, misalnya sebagai aplikasi desktop

biasa, sehingga sistem operasi bisa menjalankan prosedur main() nya. Kelas pada applet

misalnya harus juga dideklarasikan sebagai public supaya bisa diakses oleh web browser.

Jika suatu kelas tidak dideklarasikan sebagai public maka ia hanya akan bisa diakses dari

paket yang sama. Bagian ini membahas tentang paket. Kelas yang tidak ditulis dalam suatu

paket tertentu akan dimasukkan dalam paket default.

Suatu paket seharusnya terdiri dari beberapa kelas yang saling berhubungan. Beberapa dari

kelas ini memang sengaja dibuat public agar bisa diakses dari desktop atau program lain

misalnya. Bagian lain, yang merupakan bagian internal dari bagaimana paket tersebut

bekerja dan tidak boleh disentuh dari luar, tidak boleh dibuat menjadi public. Paket

adalah salah satu jenis dari kotak hitam, dan kelas public dalam paket tersebut adalah

antar muka dengan dunia luarnya.

Variabel atau metode anggota suatu kelas juga bisa dideklarasikan sebagai public yang

juga berarti bisa diakses dari manapun. Variabel atau metode anggota ini juga bisa

dideklarasikan sebagai private yang artinya hanya bisa diakses dari dalam kelas di mana

dia dideklarasikan. Membuat variabel menjadi private memastikan bahwa tidak ada

bagian lain yang akan bisa mengubah variabel ini kecuali dari dalam kelas atau objek itu

sendiri.

Jika kita tidak memberikan sifat akses pada metode atau variabel anggota tertentu, maka ia

akan otomatis bisa diakses oleh semua kelas dalam paket yang sama.

Page 157: Java With Eclipse

Ada satu jenis sifat akses lain yang bisa digunakan pada variabel atau metode anggota

kelas, yaitu protected. Sifat protected digunakan apabila kita ingin variabel atau

metode anggota tersebut bisa diakses oleh turunan kelas tersebut. Artinya lebih leluasa dari

private tapi lebih ketat daripada public. Kelas yang didesain untuk diturunkan, biasanya

memiliki anggota protected. Anggota protected digunakan untuk menambah fondasi

bagi kelas turunannya, akan tetapi tetap tak terlihat dari dunia luar.

Menggabungkan Statik dan Non-Statik

Seperti disebutkan sebelumnya, kelas dapat memiliki dua kegunaan yang sangat berbeda.

Kelas bisa digunakan untuk menggabungkan variabel dan subrutin statik. Atau juga bisa

digunakan sebagai produsen pembuat objek. Variabel dan subrutin non-statik dalam suatu

kelas akan menentukan metode dan variabel instansi pada objek yang diciptakan dari kelas

tersebut. Dalam banyak kasus, suatu kelas dapat melakukan salah satu atau kedua fungsi

tersebut secara bersamaan.

Dalam hal anggota statik dan non-statik digabung dalam satu kelas, kelas tersebut

mengharapkan adanya interaksi antara bagian statik dan bagian non-statik dari suatu kelas.

Misalnya, metode instansinya menggunakan variabel statik atau memanggil subrutin statik.

Metode instansi dimiliki oleh suatu objek, bukan oleh kelas tersebut. Karena kita bisa

membuat banyak objek dari suatu kelas, di mana setiap objek yang diciptakan memiliki

metode instansi masing-masing. Akan tetapi akan hanya ada satu variabel statik yaitu yang

dimiliki oleh suatu kelas. Dengan demikian, kita memiliki banyak objek yang bisa

mengakses variabel statik tersebut bersama-sama.

Misalnya anggap kita akan menulis kelas PasanganDadu yang menggunakan kelas Random

seperti pada bagian sebelumnya untuk mengocok dadu. Objek PasanganDadu perlu

mengakses objek Random. Akan tetapi membuat objek Random untuk setiap objek

PasanganDadu adalah terlalu berlebihan, karena fungsinya hanya digunakan untuk

menghasilkan nilai acak saja. Solusi yang bagus adalah dengan menggunakan variabel

static yang digunakan oleh semua objek yang dibuat dari kelas PasanganDadu. Misalnya

pada kode berikut ini :

class PasanganDadu {

private static Random randGen = new Random();

// (Catatan: java.util.Random telah diimpor sebelum kelas ini

dibuat)

public int dadu1; // Angka pada dadu pertama

Page 158: Java With Eclipse

public int dadu2; // Angka pada dadu kedua

public PasanganDadu() {

// Konstruktor. Membuat pasangan dadu dengan angka

// awal berupa bilangan acak

kocok();

}

public void kocok() {

// Kocok dadu dengan membuat masing-masing dadu

// bernilai bilangan acak 1 hingga 6

dadu1= randGen.nextInt(6) + 1;

dadu2= randGen.nextInt(6) + 1;

}

} // akhir kelas PasanganDadu

Contoh lain adalah kelas Murid yang digunakan pada bagian sebelumnya. Kita tambahkan

variabel instansi nomorMurid yaitu nomor unik yang berbeda untuk setiap murid. Untuk itu

kita perlu melacak nomor baru yang belum dipakai dengan variabel nomorBerikutnya

yang berbentuk variabel statik sehingga semua objek akan mengacu pada variabel yang

sama. Ketika objek baru dibuat, objek baru akan mengambil nilai nomorBerikutnya untuk

dijadikan nomorMurid yang baru.

public class Murid {

private String nama; // Nama murid

private int nomorMurid; // nomor murid unik

public double ujian1, ujian2, ujian3; // Nilai ujian

private static int nomorBerikutnya = 0;

// simpan nomor murid berikutnya

Murid(String namaBaru) {

// Konstruktor objek Murid:

// memberi nama, dan memberi nomor murid baru

nama = namaBaru;

nomorBerikutnya++;

nomorMurid = nomorBerikutnya;

}

public String getNama() {

Page 159: Java With Eclipse

// Fungsi untuk mengambil isi variabel instansi private: nama

return nama;

}

public int getNomorMurid() {

// Fungsi untuk membaca isi nomorMurid

return nomorMurid;

}

public double hitungRataRata() {

// Hitung rata-rata nilai ujian

return (ujian1 + ujian2 + ujian3) / 3;

}

} // akhir kelas Murid

Inisialisasi "nomorBerikutnya = 0" hanya dilakukan satu kali, yaitu ketika kelas ini

pertama kali dipanggil (pada saat program dijalankan). Ketika objek baru bertipe Murid

dibuat, dan di dalam konstruktor perintah "nomorBerikutnya++;", maka nomor

berikutnya akan disimpan untuk digunakan pada objek baru lainnya.

Ketika objek pertama dibuat, nilai nomorBerikutnya akan bernilai 1. Ketika objek kedua

dibuat, nilai nomorBerikutnya bernilai 2, dan seterusnya. Konstruktor akan menyimpan

nilai baru nomorBerikutnya pada variabel instansinya sendiri yang tidak di-share dengan

objek-objek lain yaitu nomorMurid. Dengan cara ini setiap murid baru akan selalu

memiliki nomorMurid baru yang berbeda satu dengan yang lain.

Bab VII - Kebenaran dan Ketangguhan Program

Kita sering sekali menemukan program komputer yang gagal. Kesalahan sedikit dapat

membuat program berperilaku tidak sesuai dengan yang diharapkan atau bahkan mati total.

Kita sendiri sering mengalaminya. Dan kita sering mendengar berita atau cerita tentang

kesalahan suatu software yang menyebabkan pesawat jatuh, sambungan telepon putus

seketika, atau bahkan (dalam kasus yang langka) menyebabkan kematian orang.

Program sebetulnya tidak sejelek yang kita pikir. Mungkin kita tidak bisa membuat

program yang sama sekali bebas dari masalah, akan tetapi pemrograman yang baik dan alat

pemrograman yang didesain dengan baik akan membantu kita membuat program dengan

masalah yang sesedikit mungkin. Bagian ini akan membahas tentang program yang

"benar" dan "tangguh". Kita juga akan melihat pengecualian (exceptions), yaitu salah satu

Page 160: Java With Eclipse

alat dalam bahasa Java yang dapat membantu kita membuat program yang "tangguh"

tersebut.

Pengenalan tentang Program yang Benar dan Tangguh

Suatu program disebut "benar" jika ia menyelesaikan suatu tugas sesuai dengan desainnya.

Program disebut "tangguh" jika ia bisa menangani suatu situasi yang tidak biasa dengan

cara yang masuk akal. Misalnya, suatu program didesain untuk membaca angka yang

diketik oleh user, kemudian menampilkan angka dengan urutan tertentu. Program tersebut

benar jika ia bekerja untuk angka berapa pun. Program tersebut tangguh jika program

tersebut bisa menangani input yang salah, misalnya jika user memasukkan sesuatu yang

bukan angka, misalnya, dengan cara memberi tahu user bahwa input yang dia masukkan

salah, dan mengabaikan input yang salah tersebut. Program yang tidak tangguh akan keluar

tiba-tiba atau memberikan keluaran yang tidak bermakna dalam kondisi tertentu.

Semua program harusnya bekerja dengan benar. (Program yang dibuat untuk mengurutkan

angka tetapi tidak mengurutkan dengan benar adalah program yang tidak berguna).

Program tidak selalu harus tangguh secara total. Akan tetapi tergantung pada siapa yang

akan menggunakan dan bagaimana program tersebut digunakan. Misalnya, program kecil

yang digunakan hanya oleh Anda sendiri tidak harus tangguh total, karena kita tahu batas-

batas dan bagaimana program tersebut bekerja.

Kebenaran suatu program sebenarnya lebih sulit dari apa yang kita bayangkan. Seorang

programmer mencoba membuat program sesuai dengan spesifikasi tentang bagaimana

sebuat program "seharusnya" bekerja. Hasil kerja programmer tersebut benar jika program

yang ia buat bekerja sesuai dengan spesifikasinya. Tapi apakah itu berarti program tersebut

benar? Bagaimana jika ternyata spesifikasinya kurang lengkap atau salah? Program yang

benar seharusnya merupakan implementasi dari spesifikasi yang lengkap dan benar.

Pertanyaannya apakah spesifikasi tersebut lengkap dan sesuai dengan yang diinginkan

tercakup di luar domain ilmu komputer.

Banyak pengguna komputer memiliki pengalaman di mana program tidak bekerja atau

crash. Dalam banyak hal, masalah tersebut hanya mengganggu saja, tapi kadang-kadang

masalahnya lebih kompleks dari itu, misalnya hilangnya data atau uang. Jika komputer

diberi tugas penting, konsekuensinya akan lebih serius apabila program tersebut

berperilaku tidak normal.

Beberapa tahun yang lalu, kegagalan dua misi ruang angkasa ke Mars masuk dalam berita.

Kedua kegagalan tersebut dipercaya karena masalah pada software, akan tetapi pada kedua

Page 161: Java With Eclipse

kasus tersebut masalahnya bukan pada program yang tidak benar. Pada bulan September

1999, Orbiter Iklim Mars terbakar di atmosfer Mars karena data yang ditulis dalam satuan

Inggris (inci, kaki, dll) dimasukkan ke dalam program komputer yang didesain untuk

menerima input satuan Metrik (sentimeter, kilometer, dll). Beberapa bulan kemudian,

Pendarat Kutub Mars jatuh karena softwarenya mematikan mesinnya terlalu cepat.

Program yang dibuat seharusnya bisa mendeteksi tumpuan ketika pesawat mendarat dan

baru kemudian mematikan mesin. Akan tetapi, roda pendarat kemungkinan macet yang

menyebabkan program pemati mesin aktif sebelum pesawat menyentuh tanah. Sistem yang

lebih tangguh akan mengecek terlebih dahulu ketinggian pesawat sebelum mesin

dimatikan.

Masih banyak beberapa kisah tentang masalah yang disebabkan oleh jeleknya desain atau

implementasi suatu software. Silakan lihat buku Computer Ethics karangan Tom Forester

dan Perry Morrison untuk melihat beberapa insiden yang pernah terjadi. (Buku ini

menceritakan tentang isu etika dalam bidang komputer. Buku ini mungkin penting sekali

untuk dibaca oleh orang yang berkecimpung dalam dunia ilmu komputer).

Pada tahun 1985 dan 1986, satu orang tewas dan beberapa lainnya terluka karena overdosis

radiasi, pada saat melakukan perawatan radiasi dengan mesin radiasi yang komputernya

tidak diprogram dengan benar. DI kasus lain, selama 10 tahun hingga tahun 1992, sekitar

1000 pasien kanker menerima dosis radiasi sekitar 30% lebih rendah dari yang diberikan

dokter karena kesalahan pemrograman.

Pada tahun 1985, sebuah komputer di Bank of New York menghancurkan data-data

transaksi sekuritas yang sedang berjalan karena adanya kesalahan pada program. Butuh

kurang dari 24 jam untuk memperbaiki program tersebut, akan tetapi pada saat itu, bank

sudah kehilangan sekitar 5 juta US dollar karena bunga overnight yang harus dipinjam

untuk mengkover masalah tersebut.

Pemrograman sistem kendali inersia dari pesawat tempur F-16 bisa membalik pesawat dari

atas ke bawah ketika digunakan di atas khatulistiwa, untungnya masalah ini sudah

ditemukan dalam simulasi. Pemindai luar angkasa Mariner 18 hilang karena kesalahan di

satu baris program. Kapsul luar angkasa Gemini V salah mendarat beberapa ratus

kilometer lebih jauh karena programmer lupa untuk memasukkan perputaran bumi ke

dalam perhitungan.

Pada tahun 1990, layanan telephon jarak jauh AT&T terganggu di seluruh Amerika Serikat

ketika program komputer yang baru dijalankan terbukti memiliki bug.

Page 162: Java With Eclipse

Contoh-contoh di atas adalah beberapa yang pernah terjadi. Masalah software adalah

masalah yang sangat umum. Sebagai programmer, kita harus mengerti kenapa itu bisa

terjadi dan bagaimana cara mengatasinya.

Salah satu bagian dari masalahnya dapat dilacak kepada bahasa pemrogramannya itu

sendiri, begitu kata para penemu Java. Java didesain untuk memberikan proteksi terhadap

beberapa jenis kesalahan. Bagaimana caranya suatu bahasa pemrograman menghindari

kesalahan? Mari kita lihat beberapa contohnya.

Bahasa pemrograman terdahulu tidak membutuhkan variabel untuk dideklarasikan. Pada

bahasa pemrograman tersebut, ketika suatu nama variabel digunakan dalam program,

variabel akan otomatis dibuat. Mungkin ini terlihat lebih mudah dan nyaman daripada

harus mendeklarasikan variabel beserta tipenya terlebih dahulu. Akan tetapi, ada

konsekuensinya : Kesalahan ketik sedikit saja akan membuat komputer menciptakan

variabel baru yang sebetulnya tidak kita inginkan. Kesalahan seperti ini pernah terjadi dan

mengakibatkan hilangnya pesawat ruang angkasa.

Dalam bahasa pemrograman FORTRAN, perintah "DO 20 I = 1,5" adalah pernyataan

pertama dari suatu perulangan. Sekarang, spasi tidak lagi suatu hal yang penting pada

bahasa FORTRAN, sehingga perintah ini akan sama dengan "DO20I=1,5". Di lain pihak,

perintah "DO20I=1.5" dengan tanda titik bukan koma, merupakan pernyataan pemberi

nilai yang memberi nilai 1.5 ke dalam variabel DO20I. Misalnya ada kesalahan dalam

mengetik koma menjadi titik, bisa jadi akan menyebabkan suatu roket meledak sebelum

diluncurkan.

Karena FORTRAN tidak memerlukan variabel untuk dideklarasi, kompilernya akan

senang menerima perintah "DO20I=1.5". Ia akan membuat variabel baru bernama DO20I.

Jika FORTRAN membutuhkan variabel untuk dideklarasikan di awal, kompiler akan

mengeluarkan pesan kesalahan di awal karena variabel DO20I tidak pernah dideklarasikan

sebelumnya.

Hampir semua bahasa pemrograman saat ini perlu mendeklarasikan variabel sebelum

digunakan, akan tetapi masih ada beberapa fitur pada bahasa pemrograman yang bisa

menyebabkan kesalahan. Java sudah membuang fitur ini. Beberapa orang tidak suka

karena ini membuat Java menjadi kurang feksibel dan kurang ampuh. Walaupun mungkin

kritik ini benar, meningkatnya tingkat keamanan dan ketangguhan suatu program mungkin

lebih dipentingkan dalam beberapa hal.

Page 163: Java With Eclipse

Pertahanan yang paling baik untuk mencegah beberapa macam jenis kesalahan adalah

mendesain bahasa pemrograman di mana membuat kesalahan tidak mungkin sama sekali.

Dalam kasus lain, di mana kesalahan tidak bisa dihilangkan sama sekali, bahasa

pemrograman bisa didesain sehingga apabila kesalahan terjadi, maka kesalahan ini akan

dapat dideteksi secara otomatis. Paling tidak cara ini akan mencegah kesalahan tersebut

membuat bencana yang lebih besar, karena akan memberi peringatan kepada programmer

bahwa ada sesuatu bug yang harus diperbaiki. Mari lihat beberapa contoh yang diberikan

Java untuk mengatasi permasalahan ini.

Suatu array dibuat dengan beberapa lokasi, dimulai dengan 0 hingga ke indeks

maksimumnya. Kita tidak dibolehkan untuk menggunakan lokasi array di luar rentang

yang sudah dibuat. Pada Java, jika kita memaksakan untuk melakukan itu, sistem akan

otomatis mendeteksi hal ini. Pada bahasa pemrograman lain seperti C dan C++,

programmer diberi keleluasaan penuh untuk memastikan bahwa indeks array berada di

dalam rentang tersebut.

Misalnya suatu array, A, memiliki tiga lokasi A[0], A[1], dan A[2]. Maka A[3], A[4], dan

berikutnya adalah lokasi pada memori di luar array tersebut. Pada Java, apabila kita

mencoba untuk menyimpan data pada A[3], Java akan mendeteksi ini. Program akan

dihentikan saat itu juga (kecuali kesalahan ini "ditangkap" yang akan dibahas kemudian).

Pada bahasa C atau C++, komputer akan diam saja dan melakukan penyimpanan di lokasi

ini. Hasilnya akan tidak bisa diprediksi. Konsekuensinya akan jauh lebih berat daripada

jika program berhenti (Kita akan diskusikan tentang tumpahan buffer di bagian ini nanti).

Pointer (penunjuk memori) juga merupakan kesalahan pemrograman yang paling sulit.

Dalam Java, variabel dari suatu objek menyimpan pointer atau rujuan ke alamat memori di

mana objek tersebut disimpan, atau isinya bisa juga null. Jika kita mencoba untuk

menggunakan nilai null seperti layaknya rujukan ke objek sungguhan, maka sistem

komputer akan mendeteksinya. Dalam bahasa pemrograman lain, lagi-lagi, adalah

tanggung jawab programmer untuk mencegah digunakannya rujukan ke null. Pada

komputer Macintosh lama, alamat null merupakan alamat ke lokasi di memori dengan

alamat 0. Program dapat menggunakan memori di dekat alamat 0. Sayangnya, Macintosh

menyimpan data penting tentang sistem di lokasi tersebut. Mengubah data di lokasi

tersebut akan membuat sistem crash atau hang, bukan hanya program tersebut saja tetapi

keseluruhan sistem operasi akan berhenti.

Kesalahan pointer lain adalah jika isi pointer menunjuk pada tipe data yang salah atau

lokasi di memori yang tidak memiliki objek sama sekali. Kesalahan seperti ini tidak

Page 164: Java With Eclipse

mungkin dalam bahasa Java, karena programmer tidak diperbolehkan untuk mengganti

pointer sama sekali. Di dalam bahasa pemrograman lain, programmer bisa mengganti

lokasi pointer ke lokasi lain, intinya, ke lokasi memori manapun. Jika tidak dilakukan

dengan benar, pointer ini bisa menunjuk pada lokasi berbahaya atau menghasilkan sesuatu

yang tidak bisa diperkirakan.

Kesalahan lain yang bisa terjadi pada Java adalah kebocoran memori. Pada Java, sewaktu

tidak ada lagi pointer yang merujuk ke pada suatu objek, objek tersebut akan diambil oleh

pemulung memori, sehingga memori tersebut dapat digunakan lagi oleh bagian program

lain. Dalam bahasa pemrograman lain, programmer bertanggung jawab untuk

mengembalikan memori yang tidak digunakan kepada sistem operasi. Jika programmer

tidak melakukannya, makan memori yang tidak terpakai akan terakumulasi, sehingga

jumlah memori yang tersedia akan berkurang. Ini adalah salah satu contoh masalah umum

yang terjadi pada komputer Windows di mana banyak sekali kebocoran memori yang

terjadi, sehingga komputer harus direstart ulang setiap beberapa hari.

Banyak program yang terjangkit masalah tumpahan buffer (buffer overflow error).

Tumpahan buffer sering menjadi berita utama karena hal ini sering mengakibatkan

kompromi masalah keamanan komputer. Ketika komputer menerima data dari komputer

lain dari network atau internet misalnya, data tersebut akan disimpan dalam buffer. Buffer

adalah bagian memori yang telah dialokasikan program untuk menyimpan data tersebut.

Tumpahan buffer terjadi jika data yang diterima lebih banyak dari jumlah data yang bisa

ditampung oleh buffer. Pertanyaannya adalah kapan ini terjadi?

Jika kesalahan ini bisa dideteksi oleh program atau program yang mengatur lalu lintas

network, maka satu-satunya kemungkinan adalah pada karena kesalahan transmisi data

pada network. Masalah utamanya terjadi ketika program tidak bisa mendeteksi tumpahan

buffer secara benar. Dalam hal ini, software terus mensuplai data ke memori meskipun

buffer telah terisi penuh, dan data lebihnya disimpan pada bagian memori yang tidak

dialokasikan untuk buffer tersebut. Bagian memori yang tertunpah tersebut mungkin

digunakan untuk fungsi lain. Mungkin juga digunakan untuk menyimpan data penting lain.

Atau bahkan mungkin menyimpan kode program itu sendiri. Ini yang akan menjadi

masalah keamanaan. MIsalnya tumpahan buffer ini menimpa bagian dari program. Ketika

komputer mengeksekusi bagian program yang telah diganti, maka sebetulnya komputer

akan menjalankan data yang diterima dari komputer lain. Data ini bisa berisi apa saja. Bisa

jadi program untuk menghentikan komputer atau bahkan mengendalikan komputer.

Programmer jahat yang bisa menemukan kesalahan tumpahan memori dalam software

Page 165: Java With Eclipse

pengendali network bisa menggunakan lubang ini untuk menjalankan program-program

jahatnya.

Untuk software yang ditulis dalam Java, kesalahan tumpahan buffer tidak dimungkinkan.

Bahasa Java tidak mungkin menyimpan data di memori yang tidak dialokasikan

kepadanya. Untuk bisa menyimpan data, komputer membutuhkan pointer yang menunjuk

pada lokasi memori yang belum terpakai, atau menggunakan lokasi array yang berada di

luar lokasi yang disediakan untuk array tersebut. Seperti dijelaskan sebelumnya, kedua

kemungkinan tersebut tidak diperbolehkan sama sekali pada Java. (Akan tetapi, masih

mungkin kesalahan seperti ini muncul pada kelas standar Java, karena beberapa metode

pada kelas ini sebenarnya ditulis dalam bahasa C bukan Java).

Sudah jelas desain bahasa bisa membantu mencegah kesalahan atau membantu mendeteksi

masalah yang mungkin terjadi. Atau dibutuhkan pengujian, misalnya menguji apakah

pointer bernilai null. Beberapa programmer mungkin merasa harus mengorbankan

kecanggihan dan efisiensi. Akan tetapi, ada banyak situasi di mana keamanan merupakan

prioritas utama. Java didesain untuk situasi seperti ini.

Ada satu bagian di mana desainer Java tidak memasukkan pendeteksi masalah secara

otomatis, yaitu perhitungan numerik. Pada Java, nilai suatu bilangan int dinyatakan dalam

bilangan biner 32-bit. Dengan 32 bit, maka terdapat kurang lebih 4 milyar bilangan yang

bisa dibentuk. Nilai int memiliki rentang antara -2147483648 hingga 2147483647. Apa

yang terjadi jika hasil perhitungan berada di luar rentang ini? Misalnya, berapa

2147483647 + 1? Dan berapa 2000000000 * 2? Jawaban yang benar secara matematis

berada di luar nilai int. Contoh-contoh di atas disebut tumpahan bilangan bulat (integer

overflow). Dalam banyak kasus, tumpahan bilangan bulat termasuk suatu kesalahan. Akan

tetapi Java tidak otomatis mendeteksi kesalahan tersebut. Misalnya, perhitungan

2147483647 + 1 akan bernilai negatif -2147483648 (Apa yang terjadi sebenarnya adalah

bit tambahan di luar bit ke-32 diabaikan. Nilai yang lebih besar dari 2147483647 akan

"terpotong" sehingga menjadi nilai negatif. Secara matematis, hasilnya akan selalu

merupakan sisa pembagian dari pembagian dengan 232).

Banyak kesalahan program yang disebabkan oleh kesalahan semacam ini. Program

tersebut benar, akan tetapi tidak bisa menangani bilangan lebih besar daripada 32 bit.

Contoh sederhana adalah kesalahan Y2K sebenarnya merupakan kesalahan yang mirip

dengan ini.

Page 166: Java With Eclipse

Untuk jenis bilangan real seperti double, masalahnya bahkan lebih kompleks lagi. Bukan

hanya tumpahan yang mungkin terjadi. Untuk jenis double, rentangnya berlaku hingga

10308. Nilai yang lebih dari nilai ini tidak "terpotong" menjadi negatif. Akan tetapi ia akan

diubah menjadi suatu konstanta yang bernilai tak berhingga. Nilai

Double.POSITIVE_INFINITY dan Double.NEGATIVE_INFINITY melambangkan nilai

positif tak hingga dan negatif tak hingga. Nilai spesial lainnya dari tipe data double adalah

Doube.NaN atau bukan bilangan (not a number), yang melambangkan suatu nilai yang

tidak berarti. Misalnya pembagian dengan 0 atau akar kuadrat suatu bilangan negatif. Kita

bisa menguji apakah suatu variabel berisi bukan bilangan dengan memanggil fungsi yang

bertipe keluaran boolean, yaitu Double.isNaN(x).

Untuk bilangan real, ada komplikasi tambahan yaitu hampir semua bilangan real hanya

bisa dilambangkan dalam bentuk pendekatan. Bilangan real bisa memiliki jumlah digit di

belakang koma yang tak terhingga banyaknya. Nilai bertipe double biasanya akurat sekitar

15 digit di belakang koma. Bilangan real 1/3, misalnya, berarti 0.33333333......, dan

bilangan ini tidak bisa digantikan dengan bilangan dengan jumlah bit terbatas. Perhitungan

dengan bilangan real biasanya memiliki kesalahan akurasi. Sebenarnya, jika kita kurang

berhati-hati, akan menyebabkan perhitungan sama sekali salah. Ada bidang tertentu dalam

ilmu komputer yang dinamakan analisis numerik yang berkonsentrasi pada algoritma untuk

memanipulasi bilangan real.

Tidak semua kesalahan yang mungkin terjadi bisa dideteksi otomatis oleh Java. Lebih jauh,

bahkan ketika suatu kesalahan bisa dideteksi secara otomatis, reaksi standar dari sistem

adalah melaporkan kesalahan dan menghentikan jalannya program. Ini bukan ciri program

yang tangguh! Sehingga programmer harus mempelajari teknik untuk mencegah dan

mengatasi kesalahan. Topik ini akan dibahas pada bab ini.

Membuat Program yang Benar

Program benar tidak jadi dengan sendirinya. Ia membutuhkan perencanaan dan perhatian

kepada detail untuk mencegah kesalahan dalam program. Ada beberapa teknik yang bisa

digunakan oleh programmer untuk meningkatkan kebenaran suatu program.

Dalam beberapa kasus, kita bisa membuktikan bahwa program tersebut benar. Yaitu

dengan menggunakan pembuktian secara matematis bahwa urutan penghitungan yang

dilakukan program akan selalu menghasilkan hasil yang benar. Pembuktian yang

Page 167: Java With Eclipse

komprehensive sangat sulit dibuat karena secara praktek pembuktian semacam ini hanya

bisa dilakukan pada program yang cukup kecil.

Seperti telah disebutkan sebelumnya, program yang benar menurut spesifikasi tidak

berguna apabila spesifikasinya salah. Artinya, bahkan dalam pemrograman sehari-hari pun,

kita harus terus mencari ide dan teknik yang bisa kita gunakan untuk membuktikan bahwa

program yang kita tulis adalah benar.

Ide dasarnya adalah proses (process) dan keadaan (state). Suatu keadaan terdiri dari semua

informasi yang terkait dengan eksekusi suatu program pada saat tertentu. Keadaan

mencakup, misalnya, nilai semua variabel pada program, keluaran yang telah diproduksi,

input yang sedang diambil, dan posisi dalam di mana program tersebut sedang dieksekusi.

Proses adalah urutan keadaan yang harus dilalui oleh komputer ketika menjalankan suatu

program.

Dari sudut pandang ini, arti suatu pernyataan dalam suatu program dapat diekspresikan

dalam akibat apa yang dihasilkan dari eksekusi suatu perintah terhadap keadaan saat itu.

Sebagai contoh sederhana, arti dari pernyataan "x = 7;" adalah setelah pernyataan ini

dieksekusi, nilai dari variabel x adalah 7. Kita bisa yakin tentang fakta ini, sehingga fakta

ini bisa dijadikan salah satu bukti matematis.

Sebenarnya, kita juga seringkali bisa menilai suatu program dan menyimpulkan bahwa

suatu fakta adalah benar pada saat tertentu pada eksekusi suatu program. Misalnya,

perhatikan perulangan berikut :

do {

N = Math.random();

} while (N <= 0.5);

Setelah perulangan selesai, kita yakin betul bahwa nilai variabel N pasti lebih besar dari

0.5. Perulangan tidak bisa berhenti jika kondisi ini tidak tercapai. Bukti ini merupakan

bagian dari arti perulangan while. Lebih umum, jika perulangan while menggunakan

pengujian "while (kondisi)" maka bisa dipastikan bahwa setelah perulangan selesai

kondisi bernilai false. Kemudian kita bisa menggunakan fakta ini untuk menyimpulkan

apa yang akan terjadi ketika eksekusi program berlanjut. (Dengan perulangan, kita juga

harus memastikan kapan perulangan tersebut akan berakhir. Hal ini harus dipikirkan lebih

lanjut secara terpisah).

Suatu fakta yang pasti benar setelah bagian program dieksekusi disebut kondisi akhir dari

bagian program tersebut. Kondisi akhir adalah fakta yang bisa kita gunakan untuk

menyimpulkan tentang perilaku suatu program. Kondisi akhir suatu program secara

Page 168: Java With Eclipse

keseluruan adalah fakta yang bisa dibuktikan ketika program selesai dieksekusi. Suatu

program bisa dibuktikan bahwa ia melakukan fungsinya dengan benar jika kondisi

akhirnya sesuai dengan spesifikasi program.

Misalnya, kita lihat potongan program berikut, di mana semua variabelnya memiliki tipe

double:

det = B*B - 4*A*C;

x = (-B + Math.sqrt(det)) / (2*A);

Persamaan kuadrat (darti matematika di SMU) menyatakan bahwa nilai x adalah solusi

persamaan A*x2 + B*x + C = 0 jika det bernilai 0 atau lebih. Jika kita menganggap atau

menjamin bahwa B*B - 4*A*C >= 0 dan A != 0, maka x yaitu solusi persamaan kuadrat

merupakan kondisi akhir.

Kita sebut B*B - 4*A*C >= 0 sebagai kondisi awal potongan program tersebut. Kondisi A

!= 0 adalah kondisi awal lainnya. Kondisi awal adalah kondisi yang harus bernilai benar

pada suatu waktu di tengah eksekusi program untuk menjamin bahwa program akan dapat

terus dieksekusi tanpa kesalahan. Kondisi awal adalah sesuatu yang kita ingin selalu benar.

Kondisi awal harus kita cek agar program kita benar.

Mari kita lihat potongan program yang lebih panjang berikut ini. Program ini

menggunakan kelas KonsolInput yang dibahas pada bagian sebelumnya.

do {

System.out.println("Masukkan A, B, dan C. B*B-4*A*C harus >= 0.");

System.out.print("A = ");

A = KonsolInput.ambilDouble();

System.out.print("B = ");

B = KonsolInput.ambilDouble();

System.out.print("C = ");

C = KonsolInput.ambilDouble();

if (A == 0 || B*B - 4*A*C < 0)

System.out.println("Input Anda tidak benar, masukkan lagi.");

} while (A == 0 || B*B - 4*A*C < 0);

det = B*B - 4*A*C;

x = (-B + Math.sqrt(det)) / (2*A);

Page 169: Java With Eclipse

Setelah perulangan berakhir, kita yakin bahwa B*B-4*A*C >= 0 dan juga A != 0. Kondisi

awal untuk dua baris terakhir sudah dipenuhi, sehingga kondisi akhir bahwa x merupakan

solusi persamaan A*x2 + B*x + C = 0 juga benar. Potongan progam ini menghitung solusi

suatu persamaan dengan benar dan bisa dibuktikan secara matematis (Sebetulnya karena

ada masalah utama dalam merepresentasi angka terutama bilangan real pada komputer, hal

ini tidak 100% benar. Algoritma ini benar, akan tetapi programnya bukan implementasi

sempurna dari algoritma ini.)

Berikut ini adalah contoh lain, di mana kondisi awal diuji dengan suatu perintah. Di bagian

awal pernyataan if, di mana solusi dihitung kemudian dicetak ke layar, kita yakin bahwa

kondisi awal telah dipenuhi. Di bagian lain, kita tahu bahwa salah satu kondisi awal tidak

bisa dipenuhi. Bagaimana pun kondisinya, program akan tetap benar.

System.out.println("Masukkan nilai A, B, dan C.");

System.out.print("A = ");

A = KonsolInput.ambilDouble();

System.out.print("B = ");

B = KonsolInput.ambilDouble();

System.out.print("C = ");

C = KonsolInput.ambilDouble();

if (A != 0 && B*B - 4*A*C >= 0) {

det = B*B - 4*A*C;

x = (-B + Math.sqrt(disc)) / (2*A);

System.out.println("Solusi persamaan A*X*X + B*X + C = 0 is " + x);

} else if (A == 0) {

System.out.println("Nilai A tidak boleh 0.");

} else {

System.out.println("Karena B*B - 4*A*C kurang dari nol, maka");

System.out.println("persamaan A*X*X + B*X + C = 0 tidak memiliki

solusi.");

}

Ketika kita menulis suatu program, akan lebih baik jika kita mencari tahu kondisi awal

suatu program dan memikirkan bagaimana program kita harus menanganinya. Sering kali,

kondisi awal suatu program bisa memberi informasi tentang bagaimana cara menulis

program.

Page 170: Java With Eclipse

Misalnya, untuk setiap referensi pada array, misalnya A[i], memiliki suatu kondisi awal.

Indeksnya harus berada di dalam rentang yang diperbolehkan pada array tersebut. Untuk

A[i], kondisi awalnya adalah 0 <= i < A.length. Komputer akan menguji kondisi ini ketika

ia mengevaluasi A[i], dan jika kondisi tidak dipenuhi, program akan dihentikan. Untuk

mencegah hal ini, kita harus menguji bahwa indeks i berada di dalam nilai yang

diperbolehkan (Sebetulnya ada lagi kondisi awal yang lain, yaitu A tidak boleh null, akan

tetapi mari kita abaikan untuk sementara waktu.) Misalnya kode berikut digunakan untuk

mencari nilai x di dalam array A :

i = 0;

while (A[i] != x) {

i++;

}

Dalam program ini, kita melihat bahwa program tersebut memiliki kondisi awal, yaitu x

harus ada di dalam array. JIka kondisi awal ini dipenuhi, maka perulangan akan berhenti

ketika A[i] == x. Akan tetapi, jika x tidak berada di dalam array, maka nilai i akan terus

dinaikkan hingga nilainya sama dengan A.length. Pada saat tersebut, referensi ke A[i]

menjadi ilegal sehingga program akan dihentikan. Untuk mencegah hal ini, kita bisa

menambahkan pengujian untuk menjamin bahwa kondisi awal untuk merujuk pada A[i]

bisa dipenuhi, yaitu :

i = 0;

while (i < A.length && A[i] != x) {

i++;

}

Sekarang, perulangan pasti akan selesai. Setelah selesai, nilai i akan bernilai i ==

A.length atau A[i] == x. Pernyataan if bisa ditambahkan di akhir perulangan untuk

menguji apa yang menyebabkan perulangan berhenti :

i = 0;

while (i < A.length && A[i] != x) {

i++;

}

if (i == A.length)

System.out.println("x berada di luar array");

else

System.out.println("x berada pada posisi " + i);

Pengecualian dan Pernyataan "try ... catch"

Page 171: Java With Eclipse

Membuat program untuk bekerja dalam kondisi ideal jauh lebih mudah daripada membuat

program yang tangguh. Program tangguh dapat tahan dari kondisi yang tidak biasa atau

dalam kondisi "pengecualian". Salah satu pendekatanya adalah dengan melihat

kemungkinan masalah yang mungkin terjadi dan menambahkan tes/pengujian pada

program tersebut untuk setiap kemungkinan masalah yang mungkin terjadi.

Misalnya, program akan berhenti jika mencoba mengakses elemen array A[i], di mana i

tidak berada di dalam rentang yang dibolehkan. Program tangguh harus dapat

mengantisipasi kemungkinan adanya indeks yang tak masuk akal dan menjaganya dari

kemungkinan itu. Misalnya bisa dilakukan dengan :

if (i < 0 || i >= A.length) {

... // Lakukan sesuatu untuk menangani indeks i diluar rentang

} else {

... // Proses elemen A[i]

}

Ada beberapa masalah yang mungkin terjadi dengan pendekatan seperti ini. Adalah hal

yang sangat sulit dan kadang kala tidak mungkin untuk mengantisipasi segala

kemungkinan yang dapat terjadi. Kadang tidak selalu jelas apa yang harus dilakukan

apabila suatu kesalahan ditemui. Untuk mengantisipasi semua masalah yang mungkin

terjadi bisa jadi membuat program sederhana menjadi lautan pernyataan if.

Java (seperti C++) membutuhkan metode alternatif yang lebih rapi dan terstruktur untuk

menghadapi masalah yang mungkin terjadi ketika program dijalankan. Metode ini disebut

sebagai penanganan pengecualian (exception-handling). Kata "pengecualian" diartikan

sesuatu yang lebih umum daripada "kesalahan". Termasuk di antaranya adalah kondisi

yang mungkin terjadi yang berada di luar aliran suatu program. Pengecualian, bisa berupa

kesalahan, atau bisa juga kasus tertentu yang kita inginkan terpisah dari algoritma kita.

Ketika pengecualian terjadi dalam eksekusi suatu program, kita sebut bahwa pengecualian

tersebut di-lempar-kan (thrown). Ketika ini terhadi, aliran program artinya terlempar dari

jalurnya, dan program berada dalam bahaya akan crash. Akan tetapi crash bisa dihindari

jika pengecualian tersebut ditangkap (catch) dan ditangani dengan cara tertentu. Suatu

pengecualian dapat dilempar oleh satu bagian program dan ditangkap oleh bagian program

lain. Pengecualian yang tidak ditangkap secara umum akan menyebabkan program

berhenti. (Lebih tepat apabila disebut thread yang melemparkan pengecualian tersebut

akan berhenti. Dalam program multithreading, mungkin saja thread lain akan terus berjalan

apabila thread yang satu berhenti.)

Page 172: Java With Eclipse

Karena program Java dijalankan di dalam interpreter, program yang crash berarti bahwa

program tersebut berhenti berjalan secara prematur. Tidak berarti bahwa interpreter juga

akan crash. Atau dengan kata lain, interpreter akan menangkap pengecualian yang tidak

ditangkap oleh program. Interpreter akan merespon dengan menghentikan jalannya

program. Dalam banyak bahasa pemrograman lainnya, program yang crash sering

menghentikan seluruh sistem hingga kita menekan tombol reset. Dalam Java, kejadian

seperti itu tidak mungkin -- yang artinya ketika hal itu terjadi, maka yang salah adalah

komputer kita, bukan program kita.

Ketika pengecualian terjadi, yang terjadi adalah program tersebut melemparkan suatu

objek. Objek tersebut membawa informasi (dalam variabel instansinya) dari tempat di

mana pengecualian terjadi ke titik di mana ia bisa ditangkap dan ditangani. Informasi ini

selalu terdiri dari tumpukan panggilan subrutin (subrutin call stack), yaitu daftar di mana

dan dari mana subrutin tersebut dipanggil dan kapan pengecualian tersebut dilemparkan.

(Karena suatu subrutin bisa memanggil subrutin yang lain, beberapa subrutin bisa aktif

dalam waktu yang sama.) Biasanya, objek pengecualian juga memiliki pesan kesalahan

mengapa ia dilemparkan, atau bisa juga memiliki data lain. Objek yang dilemparkan harus

diciptakan dari kelas standar java.lang.Throwable atau kelas turunannya.

Secara umum, setiap jenis pengecualian dikelompokkan dalam kelas turunan Throwable.

Throwable memiliki dua kelas turunan langsung, yaitu Error dan Exception. Kedua

kelas turunan ini pada akhirnya memiliki banyak kelas turunan lain. Kita juga bisa

membuat kelas pengecualian baru untuk melambangkan jenis pengecualian baru.

Kebanyakan turunan dari kelas Error merupakan kesalahan serius dalam mesin virtual

Java yang memang seharusnya menyebabkan berhentinya program karena sudah tidak

dapat diselamatkan lagi. Kita sebaiknya tidak mencoba untuk menangkap dan menangani

kesalahan jenis ini. Misalnya ClassFormatError dilempar karena mesin virtual Java

menemukan data ilegal dalam suatu file yang seharusnya berisi kelas Java yang sudah

terkompilasi. Jika kelas tersebut dipanggil ketika program sedang berjalan, maka kita tidak

bisa melanjutkan program tersebut sama sekali.

Di lain pihak, kelas turunan dari kelas Exception melambangkan pengecualian yang

memang seharusnya ditangkap. Dalam banyak kasus, pengecualian seperti ini adalah

sesuatu yang mungkin biasa disebut "kesalahan", akan tetapi kesalahan seperti ini adalah

jenis yang bisa ditangani dengan cara yang baik. (Akan tetapi, jangan terlalu bernafsu

untuk menangkap semua kesalahan hanya karena kita ingin program kita tidak crash. Jika

Page 173: Java With Eclipse

kita tidak memiliki cara untuk menanganinya, mungkin menangkap pengecualian dan

membiarkannya akan menyebabkan masalah lain di tempat lain).

Kelas Exception memiliki kelas turunan lainnnya, misalnya RuntimeException. Kelas

ini mengelompokkkan pengecualian umum misalnya ArithmeticException yang terjadi

misalnya ada pembagian dengan nol, ArrayIndexOutOfBoundsException yang terjadi

jika kita mencoba mengakses indeks array di luar rentang yang diijinkan, dan

NullPointerException yang terjadi jika kita mencoba menggunakan referensi ke null di

mana seharusnya referensi objek diperlukan.

RuntimeException biasanya menginidikasikan adanya bug dalam program yang harus

diperbaiki oleh programmer. RuntimeException dan Error memiliki sifat yang sama

yaitu program bisa mengabaikannya. ("Mengabaikan" artinya kita membiarkan program

crash jika pengecualian tersebut terjadi). Misalnya, program yang melemparkan

ArrayIndexOutOfBoundsException dan tidak menanganinya akan menghentikan

program saat itu juga. Untuk pengecualian lain selain Error dan RuntimeException

beserta kelas turunannya, pengecualian wajib ditangani.

Diagram berikut menggambarkan hirarki suatu kelas turunan dari kelas Throwable. Kelas

yang membutuhkan penanganan pengecualian wajib ditunjukkan dalam kotak merah.

Page 174: Java With Eclipse

Untuk menangkap pengecualian pada program Java, kita menggunakan pernyataan try.

Maksudnya memberi tahu komputer untuk "mencoba" (try) menjalankan suatu perintah.

Jika berhasil, semuanya akan berjalan seperti biasa. Tapi jika pengecualian dilempar pada

saat mencoba melaksanakan perintah tersebut, kita bisa menangkapnya dan menanganinya.

Misalnya,

try {

double determinan = M[0][0]*M[1][1] - M[0][1]*M[1][0];

System.out.println("Determinan matriks M adalah " + determinan);

}

catch ( ArrayIndexOutOfBoundsException e ) {

System.out.println("Determinan M tidak bisa dihitung karena ukuran M

salah.");

}

Komputer mencoba menjalankan perintah di dalam blok setelah kata "try". Jika tidak ada

pengecualian, maka bagian "catch" akan diabaikan. Akan tetapi jika ada pengecualian

ArrayIndexOutOfBoundsException, maka komputer akan langsung lompat ke dalam

blok setelah pernyataan "catch (ArrayIndexOutOfBoundsException)". Blok ini disebut

blok yang menangani pengecualian (exception handler) untuk pengecualian

ArrayIndexOutOfBoundsException". Dengan cara ini, kita mencegah program berhenti

tiba-tiba.

Mungkin kita akan sadar bahwa ada kemungkinan kesalahan lagi dalam blok di dalam

pernyataan try. Jika variabel M berisi null, maka pengecualian NullPointerException

akan dilemparkan. Dalam pernyataan try di atas, pengecualian NullPointerException

tidak ditangkap, sehingga akan diproses seperti biasa (yaitu menghentikan program saat itu

juga, kecuali pengecualian ini ditangkap di tempat lain). Kita bisa menangkap

pengecualian NullPointerException dengan menambah klausa catch lain, seperti :

try {

double determinan = M[0][0]*M[1][1] - M[0][1]*M[1][0];

System.out.println("Determinan matriks M adalah " + determinan);

}

catch ( ArrayIndexOutOfBoundsException e ) {

System.out.println("Determinan M tidak bisa dihitung karena ukuran M

salah.");

}

catch ( NullPointerException e ) {

System.out.print("Kesalahan program! M tidak ada: " + );

System.out.println( e.getMessage() );

Page 175: Java With Eclipse

}

Contoh ini menunjukkan bagaimana caranya menggunakan beberapa klausa catch. e

adalah nama variabel (bisa kita ganti dengan nama apapun terserah kita). Ingat kembali

bahwa ketika pengecualian terjadi, sebenarnya yang dilemparkan adalah objek. Sebelum

menjalankan klausa catch, komputer mengisi variabel ini dengan objek yang akan

ditangkap. Objek ini mengandung informasi tentang pengecualian tersebut.

Misalnya, pesan kesalahan yang menjelaskan tentang pengecualian ini bisa diambil dengan

metode getMessage() seperti contoh di atas. Metode ini akan mencetak daftar subrutin

yang dipanggil sebelum pengecualian ini dilempar. Informasi ini bisa menolong kita untuk

melacak dari mana kesalahan terjadi.

Ingat bahwa baik ArrayIndexOutOfBoundsException dan NullPointerException

adalah kelas turunan dari RuntimeException. Kita bisa menangkap semua pengecualian

dalam kelas RuntimeException dengan klausa catch tunggal, misalnya :

try {

double determinan = M[0][0]*M[1][1] - M[0][1]*M[1][0];

System.out.println("Determinan matriks M adalah " + determinan);

}

catch ( RuntimeException e ) {

System.out.println("Maaf, ada kesalahan yang terjadi.");

e.printStackTrace();

}

Karena objek yang bertipe ArrayIndexOutOfBoundsException maupun

NullPointerException juga bertipe RuntimeException, maka perintah seperti di atas

akan menangkap kesalahan indeks maupun kesalahan pointer kosong dan juga

pengecualian runtime yang lain. Ini menunjukkan mengapa kelas pengecualian disusun

dalam bentuk hirarki kelas. Kita bisa membuat klausa catch secara spesifik hingga tahu

persis apa yang salah, atau menggunakan klausa penangkap umum.

Contoh di atas mungkin tidak begitu realistis. Sepertinya kita jarang harus menangani

kesalahan indeks ataupun kesalahan pointer kosong seperti di atas. Masalahnya mungkin

terlalu banyak, dan mungkin kita akan bosan jika harus menulis try ... catch setiap kali

kita menggunakan array. Yang penting kita mengisi variabel tersebut dengan sesuatu yang

bukan null dan menjaga agar program tidak keluar indeks sudah cukup. Oleh karena itu

kita sebut penanganan ini tidak wajib. Akan ada banyak hal yang bisa jadi masalah.

(Makanya penanganan pengecualian tidak menyebabkan program makin tangguh. Ia hanya

Page 176: Java With Eclipse

memberikan alat yang mungkin kita gunakan dengan cara memberi tahu di mana kesalahan

mungkin muncul).

Bentuk pernyataan try sebenarnya lebih kompleks dari contoh di atas. Sintaksnya secara

umum dapat ditulis seperti:

try {

perintah

}

klausa_catch_tambahan

klausa_finally_tambahan

Ingat bahwa di sini blok ( yaitu { dan } ) diperlukan, meskipun jika perintah hanya terdiri

dari satu perintah. Pernyataan try boleh tidak dimasukkan, dan juga klausa finally boleh

ada boleh tidak. (Pernyataan try harus memiliki satu klausa finally atau catch). Sintaks

dari klausa catch adalah:

catch (nama_kelas_pengecualian nama_variabel) {

pernyataan

}

dan sintaks klausa finally adalah

finally {

pernyataan

}

Semantik dari klausa finally yaitu pernyataan di dalam klausa finally akan dijamin

untuk dilaksanakan sebagai perintah akhir dari pernyataan try, tidak peduli apakah ada

pengecualian yang dilempar atau tidak. Pada dasarnya klausa finally dimaksudkan untuk

melakukan langkah pembersihan yang tidak boleh dihilangkan.

Ada beberapa kejadian di mana suatu program memang harus melempar pengecualian.

Misalnya apabila program tersebut menemukan adanya kesalahan pengurutan atau

kesalahan lain, tapi tidak ada cara yang tepat untuk menanganinya di tempat di mana

kesalahan tersebut ditemukan. Program bisa melempar pengecualian dengan harapan di

bagian lain pengecualian ini akan ditangkap dan diproses.

Untuk melempar pengecualian, kita bisa menggunakan pernyataan throw dengan sintaks :

throw objek_pengecualian;

objek_pengecualian harus merupakan objek yang bertipe kelas yang diturunkan dari

Throwable. Biasanya merupakan kelas turunan dari kelas Exception. Dalam banyak

kasus, biasanya objek tersebut dibuat dengan operator new, misalnya :

throw new ArithmeticException("Pembagian dengan nol");

Page 177: Java With Eclipse

Parameter dalam konstruktor adalah pesan kesalahan dalam objek pengecualian.

Pengecualian bisa dilempar baik oleh sistem (karena terjadinya kesalahan) atau oleh

pernyataan throw. Pengecualian ini ditangani dengan cara yang sama. Misalnya suatu

pengecualian dilempar dari pernyataan try. Jika pernyataan try tersebut memiliki klausa

catch untuk menangani tipe pengecualian tersebut, maka klausa ini akan melompat ke

klausa catch dan menjalankan perintah di dalamnya. Pengecualian tersebut telah

ditangani.

Setelah menangani pengecualian tersebut, komputer akan menjalankan klausa finally,

jika ada. Kemudian program akan melanjutkan program seperti biasa. Jika suatu

pengecualian tidak ditangkap dan ditangani, maka pengolahan pengecualian akan

berlanjut.

Jika pengecualian dilempar pada saat eksekusi suatu subrutin dan pengecualian tersebut

tidak ditangani di dalam subrutin yang sama, maka subrutin tersebut akan dihentikan

(setelah menjalankan klausa finally, jika tersedia). Kemudian rutin yang memanggil

subrutin tersebut memiliki kesempatan untuk menangani pengecualian tersebut. Artinya,

jika subrutin tersebut dipanggil di dalam pernyataan try dan memiliki klausa catch yang

cocok, maka klausa catch tersebut akan dieksekusi dan program akan berlanjut seperti

biasa.

Lagi-lagi jika rutin tersebut tidak menangani pengecualian tersebut, rutin tersebut akan

dihentikan dan rutin yang memanggilnya akan mencoba menangani pengecualian tersebut.

Pengecualian akan menghentikan program secara keseluruhan jika keseluruhan rantai

panggil subrutin hingga main() tidak menangani pengecualian tersebut.

Suatu subrutin yang mungkin melempar pengecualian dapat memberi tahu dengan

menambahkan klausa "throws nama_kelas_pengecualian" pada definisi suatu subrutin.

Misalnya :

static double akar(double A, double B, double C)

throws IllegalArgumentException {

// Menghasilkan akar yang lebih besar

// dari persamaan kuadrat A*x*x + B*x + C = 0.

// (Lempar pengecualian jika A == 0 atau B*B-4*A*C < 0.)

if (A == 0) {

throw new IllegalArgumentException("A tidak boleh nol.");

}

else {

double diskriminan = B*B - 4*A*C;

Page 178: Java With Eclipse

if (diskriminan < 0)

throw new IllegalArgumentException("Diskriminan < nol.");

return (-B + Math.sqrt(diskriminan)) / (2*A);

}

}

Seperti kita diskusikan pada bagian sebelumnya, perhitungan dalam subrutin ini memiliki

kondisi awal di mana A != 0 dan B*B-4*A*C >= 0. Subrutin akan melempar pengecualian

dengan tipe IllegalArgumentException jika salah satu dari kondisi awal ini tidak

dipenuhi. Jika kondisi ilegal ditemukan dalam suatu subrutin, melempar pengecualian

kadang kala menjadi pilihan yang lebih bijaksana. Jika program yang memanggil subrutin

tersebut mengetahui bagaimana cara yang tepat untuk menangani pengecualian tersebut,

program tersebut dapat menangkapnya. JIka tidak, maka program akan crash dan

programmer akan tahu apa yang harus diperbaiki.

Penanganan Pengecualian Wajib

Dalam contoh-contoh sebelumnya, mendeklarasikan subrutin akar() yang dapat

melempar pengecualian IllegalArgumentException adalah sesuatu yang "sopan" untuk

pembaca rutin tersebut. Menangani IllegalArgumentException bukan sesuatu yang

"wajib" dilakukan. Subruin dapat melempar IllegalArgumentException tanpa

mengumumkan kemungkinan tersebut. Kemudian program yang memanggil rutin tersebut

bebas untuk menangkap atau membiarkan pengecualian yang dilempar oleh rutin tersebut.

Begitu juga halnya dengan tipe NullPointerException[/code, programmer bebas

untuk menangkap atau mengabaikan pengecualian tersebut.</p><p>Untuk kelas

pengecualian yang mewajibkan penanganan pengecualian, siatuasinya sedikit

berbeda. Jika suatu subrutin bisa melempar pengecualian seperti ini, maka

klausa [code]throws harus ditulis pada definisi subrutin. Jika tidak, maka compiler

akan menampilkan kesalahan sintaks.

Di sisi lain, misalnya suatu pernyataan dalam program dapat melemparkan pengecualian

yang mewajibkan penanganan pengecualian. Pernyataan tersebut bisa berasal dari

pernyataan throw yang sengaja dilemparkan atau hasil dari pemanggilan suatu subrutin

yang melempar pengecualian tersebut. Pengecualian ini harus ditangani dengan salah satu

dari dua cara, yaitu : Menggunakan pernyataan try yang memiliki klausa catch untuk

Page 179: Java With Eclipse

menangani pengecualian tersebut. Atau dengan menambahkan klausa throws di kepala

definisi subrutin.

Jika klausa throws digunakan, maka subrutin lain yang memanggil subrutin kita akan

bertanggung jawab menangani pengecualian tersebut. Jika kita tidak menangani

pengecualian tersebut dengan cara-cara di atas, maka Java akan menganggap sebagai

kesalahan sintaks.

Penanganan pengecualian menjadi wajib untuk kelas yang bukan kelas turunan dari Error

atau RuntimeException. Pengecualian yang wajib biasanya merupakan kondisi yang

berada di luar jangkauan programmer. Misalnya, input ilegal atau aksi ilegal oleh user.

Program tangguh harus bisa menangani kondisi seperti ini. Desain Java tidak

memungkinkan programmer untuk mengabaikan kondisi tersebut.

Di antara pengecualian yang mewajibkan penanganan pengecualian adalah yang

berhubungan dengan rutin input/output. Artinya kita tidak boleh menggunakan rutin ini

jika kita mengetahui bagaimana menangani pengecualian. Bagian berikutnya akan

membahas tentang contoh-contoh input/output dan penanganan pengecualian dengan lebih

gamblang.

Pemrograman dengan Pengecualian

Pengecualian bisa digunakan untuk membantu kita menulis program tangguh.

Pengecualian adalah pendekatan yang terstruktur dan terorganisir untuk membuat program

tangguh. Tanpa pengecualian, program akan dipenuhi dengan pernyataan if untuk menguji

berbagai macam kondisi kesalahan. Dengan pengecualian, kita bisa menulis program yang

algoritma yang lebih jelas, di mana kasus-kasus pengecualian akan ditangani di bagian

lain, yaitu di dalam klausa catch.

Membuat Kelas Pengecualian Baru

Ketika suatu program menemukan kondisi yang tidak biasa dan tidak ada cara yang masuk

akal untuk ditangani pada saat itu juga, program akan melempar pegecualian. Dalam

beberapa kasus, mungkin akan lebih mudah apabila pengecualian yang dilemparkan

merupakan objek dari salah satu kelas pengecualian bawaah Java, seperti

IllegalArgumentException atau IOException.

Akan tetapi, jika tidak ada kelas standar yang cukup mewakili jenis pengecualian tersebut,

programmer bisa membuat kelas pengecualian baru. Kelas baru tersebut harus diturunkan

dari kelas standar Throwable atau kelas turunannya. Secara umum, kelas baru merupakan

Page 180: Java With Eclipse

turunan dari kelas RuntimeExceptionatau kelas turunannya jika programmer tidak

mewajibkan penanganan kesalahan. Untuk membuat kelas pengecualian baru yang

mewajibkan penanganan kesalahan, programmer dapat membuat turunan dari kelas

Exception atau salah satu kelas turunannya.

Berikut ini adalah contoh suatu kelas yang merupakan turunan dari kelas Exception yang

mewajibkan penanganan kesalahan apabila ia dilemparkan :

public class KelasahanBaca extends Exception {

public KelasahanBaca(String pesan) {

// Konstruktor. Membuat objek dari KesalahanBaca yang berisi

// pesan kesalahan

super(pesan);

}

}

Kelas tersebut hanya memiliki konstruktor sehingga kita bisa membuat objek dari kelas

KesalahanBaca yang berisi pesan kesalahan. (Pernyataan "super(pesan)" memanggil

konstruktor di kelas supernya, yaitu Exception. Lihat bagian sebelumnya). Tentunya kelas

tersebut juga mewariskan metode getMessage() dan printStackTrace() dari kelas

supernya. Jika e merujuk pada objek dengan tipe KesalahanBaca maka perintah

e.getMessage() akan mengambil pesan kesalahan yang diberikan pada konstruktornya.

Apabila objek dengan tipe KesalahanBaca dilempar, ini berarti jenis kesalahan tertentu

telah terjadi. (Mungkin misalnya terjadi apabila pembacaan suatu String yang diproses

program tidak sesuai dengan format yang diharapkan).

Pernyataan throw bisa digunakan untuk melempar kesalahan dengan tipe KesalahanBaca.

Konstruktor objek ini harus memiliki pesan kesalahan, misalnya :

throw new ParseError("Ditemukan bilangan negatif ilegal.");

atau

throw new ParseError("Kata '" + word + "' bukan nama file yang benar.");

Jika pernyataan throw tidak terdapat dalam pernyataan try yang menangkap kesalahan

tersebut, maka subrutin yang melemparnya harus dideklarasikan di awal bahwa subrutin

tersebut bisa melempar KesalahanBaca, yaitu dengan menambah klausa "throws

KesalahanBaca". Misalnya :

void ambilNamaUser() throws KesalahanBaca {

. . .

}

Klausa ini tidak diperlukan apabila KesalahanBaca didefinisikan sebagai turunan dari

kelas RuntimeException, karena pengecualian ini tidak wajib untuk ditangani.

Page 181: Java With Eclipse

Suatu subrutin yang ingin menangani KesalahanBaca dapat menggunakan pernyataan try

dengan klausa catch untuk menangkap KesalahanBaca. Misalnya

try {

ambilNamaUser();

olahNamaUser();

}

catch (KesalahanBaca kb) {

. . . // Tangani kesalahan

}

Ingat bahwa karena KesalahanBaca adalah kelas turunan dari Exception, maka klausa

catch dalam bentuk "catch (Exception e)" juga akan menangkap KesalahanBaca dan

juga objek yang bertipe Exception.

Kadang-kadang, ada gunanya untuk menyimpan data dalam objek pengecualian, misalnya :

class KapalMeledak extends RuntimeException {

Kapal kapal; // Kapal yang meledak

int lokasi_x, lokasi_y; // Lokasi tempat kapal meledak

KapalMeledak(String pesan, Kapal k, int x, int y) {

// Konstruktor: Buat objek KapalMeledak yang menyimpan

// pesan kesalahan dan informasi bahwa kapal k

// meledak pada lokasi x,y pada layar

super(pesan);

kapal = k;

lokasi_x = x;

lokasi_y = y;

}

}

Di sini, objek KapalMeledak berisi pesan kesalahan dan informasi tambahan tentang kapal

yang meledak, yang bisa digunakan dalam perintah berikut:

if ( kapalUser.isTertembak() )

throw new KapalMeledak("Anda Tertembak!", kapalUser, xLok, yLok);

Ingat bahwa kondisi objek KapalMeledak mungkin bukan suatu kesalahan. Mungkin

hanya merupakan jalan lain dari alur suatu game. Pengecualian bisa juga digunakan

sebagai percabangan besar seperti ini dengan cara yang lebih rapi.

Kelas dan Subrutin Pengecualian

Kemungkinan untuk melempar pengecualian akan berguna dalam penulisan subrutin dan

kelas umum yang digunakan oleh lebih dari satu program. Dalam hal ini orang yang

menulis subrutin atau kelas tersebut tidak memiliki cara yang umum untuk menangani

Page 182: Java With Eclipse

kesalahan tersebut. Terutama karena ia tidak tahu bagaimana subrutin atau kelas tersebut

akan digunakan. Dalam kondisi seperti itu, programmer pemula biasanya lebih memilih

untuk mencetak pesan kesalahan dan melanjutkan program, akan tetapi cara ini tidak

memuaskan karena mungkin akan ada masalah di kemudian hari. Mencetak pesan

kesalahan dan menghentikan program juga bukan solusi karena program tidak

berkesempatan untuk mengatasi kesalahan tersebut.

Program yang memanggil subrutin atau menggunakan kelas tersebut perlu tahu bahwa

suatu kesalahan telah terjadi. Dalam bahasa yang tidak memiliki pengecualian, satu-

satunya alternatif adalah mengembalikan nilai khusus atau mengeset nilai variabel tertentu

untuk memberi tahu bahwa suatu kesalahan telah terjadi. Misalnya, fungsi ambilDouble()

bisa saja mengembalikan nilai NaN jika input dari user salah. Akan tetapi, cara ini hanya

efektif jika program utama mengecek nilai keluarannya. Pengecualian akan lebih rapi jika

suatu subrutin memiliki cara untuk tahu apabila suatu kesalahan telah terjadi.

Asersi

Ingat bahwa kondisi awal adalah kondisi yang harus benar pada suatu titik di dalam

program sehingga program akan berjalan benar dari titik tersebut dan seterusnya. Dalam

hal ini, ada kemungkin bahwa suatu kondisi awal mungkin tidak bisa dipenuhi. Untuk itu,

akan lebih baik jika kita letakkan pernyataan if untuk mengujinya. Pertanyaan berikutnya

adalah apa yang harus kita lalukan jika kondisi awal tidak benar? Salah satunya adalah

melempar pengecualian, yang kemudian akan menghentikan program kecuali jika

pengecualian tersebut ditangkap dan ditangani di tempat lain.

Bahasa pemrograman seperti C dan C++ memiliki fasilitas untuk menambah asersi

(assertions) dalam program. Asersi dapat berbentuk assert(kondisi), di mana kondisi

adalah ekspresi bernilai boolean. Kondisi adalah suatu kondisi awal yang harus benar pada

satu titik di dalam program. Ketika komputer menemukan asersi dalam eksekusi suatu

program, ia akan mengevaluasi kondisi tersebut. Jika kondisi tersebut salah, maka program

akan berhenti. Jika benar, maka program akan terus berjalan. Asersi dalam bentuk ini tidak

tersedia pada Java, akan tetapi sesuatu yang mirip seperti ini bisa dilakukan dengan

pengecualian.

Bentuk asersi assert(kondisi) dapat diganti dalam bahasa Java dalam bentuk :

if (kondisi == false)

throw new IllegalArgumentException("Asersi gagal.");

Kita bisa mengganti pesan kesalahan dengan pesan yang lebih baik, dan mungkin akan

lebih cantik apabila kelas pengecualiannya juga diganti dengan kelas yang lebih spesifik.

Page 183: Java With Eclipse

Asersi sangat umum digunakan dalam pengujian dan debugging. Setelah kita merilis

program kita, kita tidak ingin program kita crash. Akan tetapi banyak program yang dibuat

pada dasarnya seperti

try {

.

. // Jalankan program

.

}

catch (Exception e) {

System.out.println("Pengecualian dalam program terjadi.");

System.out.println("Harap kirimkan pesan bug kepada programmernya.");

System.out.println("Detail kesalahan:"):

e.printStackTrace();

}

Jika suatu program memiliki banyak asersi, maka akan menyebabkan program lebih

lambat. Salah satu keuntungan asersi pada C dan C++ adalah asersi bisa "dimatikan".

Dalam arti jika program dikompilasi dengan cara lain, maka asersi akan dibuang dari

dalam program utama. Versi rilis dari program dikompilasi dengan asersi yang dimatikan.

Dengan cara ini versi rilis akan lebih efisien, karena komputer tidak perlu mengevaluasi

semua asersi tersebut. Keuntungan lainnya adalah kita tidak perlu membuang asersi

tersebut dari kode sumber programnya.

Ada cara seperti ini yang mungkin juga bisa diterapkan pada Java, yang tergantung dari

seberapa canggih kompiler kita. Misalnya kita tentukan suatu konstanta

static final boolean DEBUG = true;

dan kita menulis asersi seperti

if (DEBUG == true && kondisi <span style="color: #00bb00;"></span>==

false)

throw new IllegalArgumentException("Asersi Gagal.");

Karena DEBUG bernilai true, maka nilai "DEBUG == true && kondisi == false" sama

dengan nilai kondisi, sehingga pernyataan if ini sama dengan pengujian suatu kondisi

awal. Sekarang misalnya kita telah selesai melakukan debugging. Sebelum kita

mengkompilasi versi rilis suatu program, kita ganti definisi DEBUG menjadi

static final boolean DEBUG = false;

Sekarang, nilai "DEBUG == true && kondisi == false" selalu bernilai false, dan

kompiler canggih akan bisa mendeteksi ini pada saat kompilasi. Karena nilai if ini akan

selalu bernilai false, kompiler canggih akan mengabaikan perintah ini dalam hasil

Page 184: Java With Eclipse

kompilasinya, karena if ini tidak akan pernah dieksekusi. Hasilnya, kode ini tidak akan

dimasukkan ke dalam versi rilis program. Dan kita hanya cukup mengganti satu baris saja

pada kode sumbernya.

Bab VIII - Pengenalan Struktur Data dan Algoritma

Komputer bisa menjadi canggih seperti sekarang karena struktur data. Struktur dala adalah

koleksi dari suatu data yang saling berhubungan. Suatu objek adalah struktur data, tapi tipe

struktur data seperti ini -- yang memiliki sedikit variabel instansi -- hanya awalnya saja.

Dalam banyak hal, programmer akan membuat struktur data kompleksnya sendiri, yaitu

dengan menghubungkan objek satu sama lain.

Bab ini akan membahas tentang array dan beberapa struktur data dalam Java.

Array

Array adalah struktur data yang terdiri dari item berurutan, di mana semua itemnya bertipe

sama. Dalam Java, item dalam array selalu dinomori dari nol hingga nilai maksimum

tertentu, yang nilainya ditentukan pada saat array tersebut dibuat. Misalnya, suatu array

berisi 100 bilangan bulat, yang dinomori dari nol hingga 99. Item dalam array bisa bertipe

salah satu tipe Java primitif. Item-item ini bisa juga berupa referensi ke objek, sehingga,

misalnya kita bisa membuat array yang berisi semua komponen dalam applet.

Bagian ini akan membahas bagaimana array dibuat dan digunakan pada Java. Juga

mencakup kelas standar java.util.ArrayList. Suatu objek bertipe ArrayList sangat

mirip dengan array dari Object, akan tetapi ia bisa bertambah ukuran secara dinamis.

Membuat dan Menggunakan Array

Jika sekumpulan data digabungkan dalam satu unit, hasilnya adalah suatu struktur data.

Data struktur dapat berupa struktur yang sangat kompleks, akan tetapi dalam banyak

aplikasi, data struktur yang cocok hanya terdiri dari kumpulan data berurutan. Struktur data

sederhana seperti ini bisa berupa array atau record.

Istilah "record" sebetulnya tidak digunakan pada Java. Suatu record pada intinya mirip

dengan objek pada Java yang hanya memiliki variabel instansi tanpa metode instansi.

Beberapa bahasa pemrograman lain yang tidak mendukung objek biasanya mendukung

record. Dalam bahasa C yang bukan bahasa berorientasi objek, misalnya, memiliki tipe

data record, dimana pada C disebut "struct". Data pada record -- dalam Java, adalah

variabel instansi suatu objek -- disebut field suatu record. Masing-masing item disebut

nama field. Dalam Java, nama field adalah nama variabel instansi. Perbedaan sifat dari

suatu record adalah bahwa item pada record dipanggil berdasarkan namanya, dan field

yang berbeda dapat berupa tipe yang berbeda. Misalnya, kelas Orang didefisikan sebagai :

Page 185: Java With Eclipse

class Orang {

String nama;

int nomorID;

Date tanggalLahir;

int umur;

}

maka objek dari kelas Orang bisa disebut juga sebagai record dengan 4 field. Nama

fieldnya adalah nama, nomorID, tanggalLahir dan umur. Lihat bahwa tipe datanya

berbeda-beda yaitu String, int, dan Date.

Karena record hanya merupakan bagian lebih kecil dari objek, kita tidak akan bahas lebih

lanjut di sini.

Seperti record, suatu array adalah kumpulan item. Akan tetapi, item pada record dipanggil

dengan nama, sedangkan item pada array dinomori, dan masing-masing item dipanggil

besarkan nomor posisi pada array tersebut. Semua item pada array harus bertipe sama.

Definisi suatu array adalah : kumpulan item bernomor yang semuanya bertipe sama.

Jumlah item dalam suatu array disebut panjang array. Nomor posisi dari array disebut

indeks item tersebut dalam array. Tipe dari item tersebut disebut tipe dasar dari array.

Tipe dasar suatu array bisa berupa tipe Java apa saja, baik berupa tipe primitif, nama kelas,

atau nama interface. Jika tipe dasar suatu array adalah int, maka array tersebut disebut

"array int". Suatu array bertipe String disebut "array String". Akan tetapi array bukan

urutan int atau urutan String atau urutan nilai bertipe apapun. Lebih baik jika array adalah

urutan variabel bertipe int atau String atau tipe lainnya.

Seperti biasa, ada dua kemungkinan kegunaan variabel : sebagai nama suatu lokasi di

memori, dan nama suatu nilai yang disimpan pada lokasi memori. Setiap posisi pada array

bersifat seperti variabel. Setiap posisi dapat menyimpan nilai dengan tipe tertentu (yaitu

tipe dasar array). Isinya bisa diganti kapanpun. Nilai tersebut disimpan di dalam array.

Array merupakan kontainer bukan kumpulan nilai.

Item pada array (maksudnya setiap anggota variabel dalam array tersebut) sering juga

disebut elemen array. Dalam Java, elemen array selalu dinomori mulai dari nol. Yaitu,

indeks dari elemen pertama suatu array adalah nol. Jika panjang array adalah N, maka

indeks elemen terakhir adalah N-1. Sekali array dibuat, maka panjangnya tidak bisa diubah

lagi.

Dalam Java, array adalah objek. Ada beberapa konsekuensi yang lahir dari fakta ini. Array

harus dibuat dengan operator new. Variabel tidak bisa menyimpan array; variabel hanya

bisa merujuk pada array. Variabel lain yang bisa merujuk array juga bisa bernilai null

Page 186: Java With Eclipse

yang berarti ia tidak merujuk pada lokasi memori apapun. Seperti objek lain, array juga

bagian dari suatu kelas, di mana seperti kelas lain adalah kelas turunan dari kelas Object.

Elemen array pada dasarnya adalah variabel instansi dalam objek array, kecuali mereka

dipanggil dalam indeksnya bukan namanya.

Meskipun array berupa objek, ada beberapa perbedaan antara array dan objek lainnya, dan

ada beberapa fitur khusus Java untuk membuat dan menggunakan array.

Misalnya A adalah variabel yang merujuk pada suatu array. Maka indeks k di dalam A bisa

dipanggil dengan A[k]. Item pertama adalah A[0], yang kedua adalah A[i], dan seterusnya.

A[k] adalah suatu variabel dan bisa digunakan seperti variabel lainnya. Kita bisa

memberinya nilai, bisa menggunakannya dalam ekspresi, dan bisa diberikan sebagai

parameter pada subrutin. Semuanya akan didiskusikan di bawah nanti. Untuk sekarang

ingat sintaks berikut

variabel_array [ekspresi_integer]

untuk merujuk pada suatu array.

Meskipun setiap array merupakan suatu objek, kelas array tidak harus didefinisikan

sebelumnya. Jika suatu tipe telah ada, maka kelas array dari tipe tersebut otomatis ada. Jika

nama suatu tipe adalah TipeDasar, maka nama kelas arraynya adalah TipeDasar[].

Artinya, suatu objek yang diciptakan dari kelas TipeDasar[] adalah array dari item yang

tiap itemnya bertipe TipeDasar. Tanda kurung "[]" dimaksudkan untuk mengingat sintaks

untuk mengambil item di dalam suatu array. "TipeDasar[]" dibaca seperti "array

TipeDasar". Mungkin perlu juga dijelaskan bahwa jika KelasA adalah kelas turunan dari

KelasB maka KelasA[] otomatis menjadi kelas turunan KelasB[].

Tipe dasar suatu array dapat berupa tipe apapun yang ada atau sudah didefinisikan pada

Java. Misalnya tipe primitif int akan diturunkan kelas array int[]. Setiap elemen di

dalam array int[] adalah variabel yang memiliki tipe int dan bisa diisi dengan nilai

dengan tipe int. Dari kelas yang bernama String diturunkan tipe array String[]. Setiap

elemen di dalam array String[] adalah variabel dengan tipe String, yang bisa diisi

dengan nilai bertipe String. Nilai ini bisa null atau referensi ke objek yang bertipe

String (dan juga kelas turunan dari String)

Mari kita lihat contoh lebih konkrotnya menggunakan array bilangan bulat sebagai contoh

pertama kita. Karena int[] adalah sebuah kelas, maka kita bisa menggunakannya untuk

mendeklarasikan variabel. Misalnya,

int[] daftar;

Page 187: Java With Eclipse

yang membuat variabel bernama daftar dengan tipe int[]. Variabel ini bisa menunjuk

pada array int, akan tetapi nilai awalnya adalah null (jika merupakan variabel anggota

suatu kelas) atau tak tentu (jika merupakan variabel lokal di dalam suatu metode). Operator

new digunakan untuk membuat objek array baru, ayng kemudian bisa diberikan kepada

daftar. Sintaksnya sama seperti sintaks sebelumnya, yaitu :

daftar = new int[5];

membuat array 5 buah integer. Lebih umum lagi, konstruktor "new TipeDasar[N]"

digunakan untuk membuat array bertipe TipeDasar[]. Nilai N di dalam kurung

menyatakan panjang array, atau jumlah elemen yang bisa ditampung. Panjang array adalah

variabel instansi di dalam objek array, sehingga array tahu berapa panjangnya. Kita bisa

mendapatkan panjang suatu array, misalnya daftar menggunakan daftar.length (akan

tetapi kita tidak bisa mengubahnya)

Hasil dari pernyataan "daftar = new int[5];" dapat diilustrasikan sebagai berikut

Perlu dicatat bahwa array integer secara otomatis diisi dengan nol. Dalam Java, array yang

baru dibuat akan selalu diisi dengan nilai tertentu: nol untuk angka, false untuk boolean,

karakter dengan nilai Unicode 0 untuk char dan null untuk objek.

Elemen di dalam array daftar dapat dirujuk dengan daftar[0], daftar[1], daftar[2],

daftar[3], dan daftar[4] (ingat juga bahwa nilai indeks terbesar adalah panjangnya

array dikurang satu). Akan tetapi, referensi array sebetulnya lebih umum lagi. Tanda

kurung di dalam referensi array bisa berisi ekspresi apapun yang nilainya suatu integer.

Misalnya jika idks adalah variabel bertipe int, maka daftar[idks] dan

daftar[2*idks+3] secara sintaks benar.

Contoh berikut akan mencetak semua isi integer di dalam array daftar ke layar :

for (int i = 0; i < daftar.length; i++) {

System.out.println( daftar[i] );

Page 188: Java With Eclipse

}

Perulangan pertama adalah ketika i = 0, dan daftar[i] merujuk pada daftar[0]. Jadi

nilai yang disimpan pada variabel daftar[0] akan dicetak ke layar. Perulangan kedua

adalah i = 1, sehingga nilai daftar[i] dicetak. Perulangan berhenti setelah mencetak

daftar[4] dan i menjadi sama dengan 5, sehingga kondisi lanjutan "i <

daftar.length" tidak lagi benar. Ini adalah contoh umum dari menggunakan perulangan

untuk mengolah suatu array.

Penggunaan suatu variabel dalam suatu program menyatakan lokasi di memori. Pikirkan

sesaat tentang apa yang akan komputer lakukan ketika ia menemukan referensi ke elemen

suatu array daftar[k] ketika program berjalan. Komputer harus menentukan lokasi

memori di mana ia dijadikan referensi. Untuk komputer, daftar[k] berarti : "Ambil

pointer yang disimpan di dalam variabel daftar. Ikuti pointer ini untuk mencari objek

array. Ambil nilai k. Pergi ke posisi ke-k dari array tersebut, dan di sanalah alamat memori

yang Anda ingin."

Ada dua hal yang bisa salah di sini. Misalnya nilai daftar adalah null. Dalam kasus ini,

maka daftar tidak memiliki referensi sama sekali. Percobaan merujuk suatu elemen pada

suatu array kosong adalah suatu kesalahan. Kasus ini akan menampilkan pesan kesalahan

"pointer kosong". Kemungkinan kesalahan kedua adalah jika daftar merujuk pada suatu

array, akan tetapi k berada di luar rentang indeks yang legal. Ini akan terjadi jika k < 0

atau jika k >= daftar.length. Kasus ini disebut kesalahan "indeks array keluar batas".

Ketika kita menggunakan array dalam program, kita harus selalu ingat bahwa kedua

kesalahan tersebut mungkin terjadi. Dari kedua kasus di atas, kesalahan indeks array keluar

batas adalah kesalahan yang lebih sering terjadi.

Untuk suatu variabel array, seperti variabel lainnya, kita bisa mendeklarasikan dan

mengisinya dengan satu langkah sekaligus, misalnya :

int[] daftar = new int[5];

Jika daftar merupakan variabel lokal dalam subrutin, maka perintah di atas akan persis

sama dengan dua perintah :

int[] daftar;

daftar = new int[5];

(Jika daftar adalah variabel instansi, tentukan kita tidak bisa mengganti "int[] daftar

= new int[5];" dengan "int[] daftar; daftar = new int[5];" karena ini hanya

bisa dilakukan di dalam subrutin)

Page 189: Java With Eclipse

Array yang baru dibuat akan diisi dengan nilai awal yang tergantung dari tipe dasar array

tersebut seperti dijelaskan sebelumnya. Akan tetapi Java juga menyediakan cara untuk

memberi isi array baru dengan daftar isinya. Dalam pernyataan yang untuk membuat array,

ini bisa dilakukan dengan menggunakan penginisialiasi array (array initializer), misalny :

int[] daftar = { 1, 4, 9, 16, 25, 36, 49 };

akan membuat array baru yang berisi 7 nilai, yaitu 1, 4, 9, 16, 25, 36, dan 49, dan mengisi

daftar dengan referensi ke array baru tersebut. Nilai daftar[0] berisi 1, nilai daftar[1]

berisi 4, dan seterusnya. Panjang daftar adalah 7, karena kita memberikan 7 nilai awal

kepada array ini.

Suatu penginisialisasi array memiliki bentuk daftar angka yang dipisahkan dengan koma

dan diapit dengan tanda kurung kurawal {}. Panjang array tersebut tidak perlu diberikan,

karena secara implisit sudah bisa ditentukan dari jumlah daftar angkanya. Elemen di dalam

penginisialisasi array tidak harus selalu berbentuk konstanta. Juga bisa merupakan variabel

atau ekspresi apa saja, asalkan nilainya bertipe sama dengan tipe dasar array tersebut.

Misalnya, deklarasi berikut membuat array dari delapan jenis Color beberapa warna telah

dibentuk dengan ekspresi "new Color(r,g,b);"

Color[] palette =

{

Color.black,

Color.red,

Color.pink,

new Color(0,180,0), // hijau gelap

Color.green,

Color.blue,

new Color(180,180,255), // biru muda

Color.white

};

Inisialisasi array bentuk seperti ini hanya bisa digunakan dalam deklarasi suatu variabel

baru, akan tetapi tidak bisa digunakan seperti operator pemberi nilai (=) di tengah-tengah

suatu program. Akan tetapi ada cara lain yang bisa digunakan sebagai pernyataan

pemberian nilai atau diberikan ke dalam subrutin. Yaitu menggunakan jenis lain dari

operator new untuk membuat atau menginisialisasi objek array baru. (Cara ini agak kaku

dengan sintaks aneh, seperti halnya sintaks kelas anonim yang telah didiskusikan

sebelumnya). Misalnya untuk memberi nilai kepada suatu variabel daftar, kita bisa

menggunakan :

daftar = new int[] { 1, 8, 27, 64, 125, 216, 343 };

Page 190: Java With Eclipse

Sintaks umum dari bentuk operator new seperti ini adalah

new TipeDasar [ ] { daftar_nilai_nilai }

Ini adalah suatu ekspresi yang isinya merupakan objek, dan bisa digunakan untuk banyak

situasi di mana suatu objek dengan tipe TipeDasar dipentingkan. Misalnya buatTombol

merupakan metode yang mengambil array String sebagai parameter, maka kita bisa

menuliskan

buatTombol( new String[] { "Stop", "Jalan", "Berikut", "Sebelum" } );

Catatan terakhir : untuk alasan sejarah, maka deklarasi

int[] daftar;

akan bernilai sama dengan

int daftar[];

di mana sintaks tersebut digunakan dalam bahasa C dan C++. Akan tetapi sintaks alternatif

ini tidak begitu masuk akan dalam Java, atau mungkin lebih baik dihindari. Lagian,

maksudnya adalah mendeklarasikan variabel dengan tipe tertentu dan namanya adalah

int[]. Akan lebih masuk akan untuk mengikuti siintaks "nama_tipe nama_variabel"

seperti pada bentuk bertama.

Pemrograman dengan Array

Array merupakan jenis struktur data yang sangat dasar dan sangat penting. Teknik

pengolahan array merupakan teknik pemrograman yang paling penting yang kita harus

kuasai. Dua jenis teknik pengolahan array -- pencarian dan pengurutan -- akan dibahas

kemudian. Bagian ini akan memperkenalkan beberapa ide dasar pengolahan array secara

umum.

Dalam banyak hal, pengolahan array berarti menggunakan operasi yang sama kepada

setiap elemen di dalam array. Biasanya sering dilakukan dengan perulangan for.

Perulangan untuk mengolah semua elemen dalam array A dapat ditulis dalam bentuk :

// lakukan inisialiasi yang diperlukan sebelumnya

for (int i = 0; i < A.length; i++) {

. . . // proses A[i]

}

Misalnya, A adalah array dengan tipe double[]. Misalnya kita ingin menjumlah semua

nilai dalam array tersebut. Algoritma umum untuk melakukannya adalah :

Mulai dengan 0;

Tambah A[0]; (proses elemen pertama di dalam A)

Tambah A[1]; (proses elemen kedua di dalam A)

.

.

Page 191: Java With Eclipse

.

Tambah A[ A.length - 1 ]; (proses elemen terakhir di dalam A)

Dengan menggunakan pengetahuan yang kita telah pelajari tentang perulangan, kita bisa

ubah algoritma di atas menjadi bentuk perulangan for seperti berikut:

double jumlah; // Jumlah nilai di dalam A

jumlah = 0; // Mulai dengan 0

for (int i = 0; i < A.length; i++)

jumlah += A[i]; // tambah A[i] ke dalam jumlah untuk i = 0, 1, ...,

A.length - 1

Lihat bahwa kondisi kelanjutan "i < A.length" menyatakan bahwa nilai i terakhir yang

akan diolah adalah A.length - 1 yaitu elemen terakhir dalam array. Ingat bahwa kita

menggunakan "<" bukan "<=" karena dengan "<=" komputer akan memberikan kesalahan

indeks di luar batas.

Pada akhirnya, nanti Anda akan bisa membuat perulangan seperti di atas di luar kepala.

Kita akan lihat beberapa contohnya. Di sini perulangan akan menghitung banyaknya

elemen di dalam array A yang nilainya kurang dari nol :

int hitung; // Untuk menghitung elemen

hitung = 0; // Mulai dengan nol

for (int i = 0; i < A.length; i++) {

if (A[i] < 0.0) // Jika elemen ini kurang dari nol

hitung++; // tambah hitung dengan 1

}

// Di sini nilai "hitung" adalah banyaknya elemen yang kurang dari 0.

Kita bisa mengganti "A[i] < 0.0" jika kita ingin menghitung banyaknya elemen di dalam

array yang memiliki sifat tertentu. Variasinya akan memiliki tema yang sama. Misalnya

kita ingin menghitung banyaknya elemen di dalam array A yang sama dengan elemen

sesudahnya. Elemen setelah A[i] adalah A[i+1], sehingga kita bisa mengganti klausa if

dengan "if (A[i] == A[i+1])". Akan tetapi tunggu dulu : Tes ini tidak bisa digunakan

apabila A[i] adalah elemen terakhir dalam array, karena tidak ada lagi array sesudahnya.

Komputer akan menolak pernyataan ini. Sehingga kita harus berhenti satu elemen sebelum

array terakhir, sehingga menjadi

int hitung = 0;

// lihat kondisi for berubah dibandingkan dengan contoh sebelumnya

for (int i = 0; i < A.length - 1; i++) {

if (A[i] == A[i+1])

hitung++;

}

Page 192: Java With Eclipse

Masalah umum lainnya adalah mencari nilai terbesar di dalam array A. Strateginya adalah

lihat semua isi array, catat nilai terbesar saat itu. Kita akan simpan nilai terbesar yang kita

temui dalam variabel maks. Pada saat kita melihat elemen array satu per satu, kapanpun

kita melihat nilai elemen tersebut lebih besar dari maks kita akan mengganti nilai maks

dengan nilai yang lebih besar tersebut. Setelah semua elemen array diproses, maka maks

merupakan nilai elemen terbesar di dalam array tersebut. Pertanyaannya adalah, apa nilai

awal maks? Salah satu kemungkinannya adalah mulai dengan nilai maks sama dengan

A[0], baru kemudian melihat isi elemen array lainnya mulai dengan A[1]. Misalnya,

double maks = A[0]; // nilai maks berisi elemen array pertama

for (int i = 1; i < A.length; i++) { // i mulai dari elemen kedua

if (A[i] > maks)

max = A[i];

}

// Di sini maks berisi nilai elemen array yang paling besar

(Ada masalah yang lebih penting di sini. Java membolehkan array memiliki panjang nol.

Artinya bahkan A[0] pun tidak ada di dalam array, sehingga memanggil A[0] akan

menghasilkan kesalahan indeks keluar batas. Akan tetapi array biasanya array dengan

panjang nol biasanya sesuatu yang kita ingin hindarkan dalam kehidupan sehari-hari.

Lagian apa artinya mencari nilai terbesar di dalam suatu array yang panjangnya nol?)

Contoh terakhir dari operasi array, misalnya kita ingin mengkopi suatu array. Untuk

mengkopi array A, tidak cukup untuk menggunakan perintah

double[] B = A;

karena perintah ini tidak membuat objek array baru. Yang dibuat di sini adalah variabel

baru yang merujuk pada objek yang sama dengan A. (Sehingga perubahan yang terjadi

pada A[i] akan juga menyebabkan B[i] berubah). Untuk membuat array baru yang

merupakan kopi dari array A, kita harus membuat objek array baru, dan mengkopi isinya

satu per satu dari array A ke array baru, sehingga

// Buat objek array baru, yang panjangnya sama dengan panjang A

double[] B = new double[A.length];

for (int i = 0; i < A.length; i++)

B[i] = A[i]; // Kopi setiap elemen dari A ke B

Mengkopi nilai dari satu array ke array yang lain adalah operasi umum sehingga Java

memiliki subrutin untuk melakukannya, yaitu System.arraycopy(), yang merupakan

subrutin anggota statik dari kelas standar System. Deklarasinya memiliki bentuk seperti :

public static void arraycopy(Object arraySumber, int indeksAwalSumber,

Page 193: Java With Eclipse

Object arrayTujuan, int indeksAwalTujuan, int jumlah)

di mana arraySumber dan arrayTujuan bisa berbentuk array dengan tipe apapun. Nilai

akan dikopi dari arraySumber ke arrayTujuan. jumlah adalah berapa banyak elemen

yang akan dikopi. Nilai akan dikopi dari arraySumber mulai dari posisi

indeksAwalSumber dan akan disimpan pada arrayTujuan mulai dari posisi

indeksAwalTujuan. Misalnya kita akan mengkopi array A, maka kita bisa menggunakan

perintah

double B = new double[A.length];

System.arraycopy( A, 0, B, 0, A.length );

Suatu tipe array, misalnya double[] adalah tipe Java biasa, sehingga kita bisa

menggunakannya seperti tipe-tipe Java lainnya. Termasuk juga digunakan sebagai

parameter formal di dalam suatu subrutin. Juga bisa digunakan sebagai tipe keluaran suatu

fungsi. Misalnya, kita bisa menulis fungsi yang membuat kopi array dengan tipe double

sebagai berikut :

double[] kopi( double[] sumber ) {

// Membuat dan mengembalikan kopi array sumber

// Jika sumber null, maka kembalikan null

if ( sumber == null )

return null;

double[] kpi; // Kopi array sumber

kpi = new double[sumber.length];

System.arraycopy( sumber, 0, kpi, 0, sumber.length );

return kpi;

}

Rutin main() memiliki parameter dengan tipe String[] yang merupakan array String.

Ketika sistem memanggil rutin main(), string di dalam array ini adalah parameter dari

baris perintah. Jika kita menggunakan konsol, user harus mengetikkan perintah untuk

menjalankan program. User bisa menambahkan input tambahan dalam perintah ini setelah

nama program yang akan dijalankan.

Misalnya, jika kelas yang memiliki rutin main() bernama programKu, maka user bisa

menjalankan kelas tersebut dengan perintah "java programKu" di konsol. Jika kita tulis

dengan "java programKu satu dua tiga", maka parameter dari baris perintahnya adalah

"satu", "dua", dan "tiga". Sistem akan memasukkan parameter-parameter ini ke dalam

array String[] dan memberikan array ini pada rutin main().

Page 194: Java With Eclipse

Berikut ini adalah contoh program sederhana yang hanya mencetak parameter dari baris

perintah yang dimasukkan oleh user.

public class CLDemo {

public static void main(String[] args) {

System.out.println("Anda memasukkan " + args.length

+ " parameter dari baris perintah");

if (args.length > 0) {

System.out.println("Parameter tersebut adaah :");

for (int i = 0; i < args.length; i++)

System.out.println(" " + args[i]);

}

} // akhir main()

} // akhir kelas CLDemo

Perhatikan bahwa parameter args tidak mungkin null meskipun tidak ada parameter yang

dimasukkan. Jika tidak ada parameter dari baris perintah yang dimasukkan, maka panjang

array ini adalah nol.

Hingga sekarang, contoh yang telah diberikan adalah bagaimana mengolah array dengan

mengakses elemennya secara berurutan (sequential access). Artinya elemen-elemen array

diproses satu per satu dalam urutan dari awal hingga akhir. Akan tetapi salah satu

keuntungan array adalah bahwa array bisa digunakan untuk mengakses elemennya secara

acak, yaitu setiap elemen bisa diakses kapan saja secara langsung.

Misalnya, kita ambil contoh suatu masalah yang disebut dengan masalah ulang tahun:

Misalnya ada N orang di dalam suatu ruangan. Berapa kemungkinan dua orang di dalam

ruangan tersebut memiliki ulang tahun yang sama (yang dilahirkan pada tanggal dan bulan

yang sama, meskipun tahunnya berbeda)? Kebanyakan orang salah menerka jawabannya.

Sekarang kita lihat dengan versi masalah yang berbeda: Misalnya kita memilih orang

secara acak dan menanyakan ulang tahunnya. Berapa orang yang Anda harus tanya untuk

mendapatkan hari ulang tahun yang sama dengan orang sebelumnya?

Tentunya jawabannya akan tergantung pada faktor yang bersifat acak, akan tetapi kita bisa

simulasikan dengan program komputer dan menjalankan beberapa kali hingga kita tahu

berapa kira-kira orang harus dicek.

Untuk mensimulasikan percobaan ini, kita harus mencatat semua ulang tahun yang kita

sudah tanyakan. Ada 365 kemungkinan hari ulang tahun (Kita abaikan sementara tahun

kabisat). Untuk setiap kemungkinan hari ulang tahun, kita perlu tahu, apakah hari ulang

Page 195: Java With Eclipse

tahun tersebut telah digunakan? Jawabannya adalah nilai boolean true atau false. Untuk

menyimpan data ini, kita bisa gunakan array dari 365 nilai boolean:

boolean[] sudahDitanya;

sudahDitanya = new boolean[365];

Tanggal-tanggal pada satu tahun dinomori dari 0 hingga 364. Nilai sudahDitanya[i] akan

bernilai true jika orang yang kita tanya berulang tahun pada hari tersebut. Pada awalnya

semua nilai pada array sudahDitanya[i] bernilai false. Ketika kita memilih satu orang

dan menanyakan hari ulang tahunnya, misalnya i, kita akan mengecek terlebih dahulu

apakah sudahDitanya[i] bernilai true. Jika tidak maka orang ini adalah orang kedua

dengan ulang tahun yang sama. Artinya kita sudah selesai.

Jika sudahDitanya[i] bernilai false, maka belum ada orang sebelum ini yang memiliki

hari ulang tahun tersebut. Kita akan ganti sudahDitanya[i] dengan true, kemudian kita

akan tanyakan lagi kepada orang lain, dan begitu seterusnya hingga semua orang di dalam

ruangan ditanyakan.

static void masalahUlangTahun() {

// Melakukan simulasi dengan memilih seseorang secara acak

// dan mengecek hari ulang tahunnya. Jika hari ulang tahunnya

// sama dengan orang yang pernah kita tanya sebelumnya,

// hentikan program dan laporkan berapa orang yang sudah dicek

boolean[] sudahDitanya;

// Untuk mencatat ulang tahun yang sudah ditanyakan

// Nilai true pada sudahDitanya[i] berarti orang lain

// sudah ada yang berulang tahun pada hari i

int hitung;

// Jumlah orang yang sudah pernah ditanya

sudahDitanya = new boolean[365];

// Awalnya, semua nilai adalah false

hitung = 0;

while (true) {

// Ambil ulang tahun secara acak dari 0 hingga 364

// Jika ulang tahun telah ditanya sebelumnya, keluar

// Jika tidak catat dalam array

Page 196: Java With Eclipse

int ultah; // Ulang tahun seseorang

ultah = (int)(Math.random()*365);

hitung++;

if ( sudahDitanya[ultah] )

break;

sudahDitanya[ultah] = true;

}

System.out.println("Ulang tahun yang sama ditemukan setelah

menanyakan "

+ hitung + " orang.");

} // akhir masalahUlangTahun()

Subrutin ini menggunakan fakta bahwa array boolean yang baru dibuat memiliki seluruh

elemen yang bernilai false. Jika kita ingin menggunakan array yang sama untuk simulasi

kedua, kita harus mereset ulang semua elemen di dalamnya menjadi false kembali dengan

perulangan for

for (int i = 0; i < 365; i++)

sudahDitanya[i] = false;

Array paralel adalah menggunakan beberapa array dengan indeks yang sama. Misalnya

kita ingin membuat beberapa kolom secara paralel -- array x di kolom pertama, array y di

kolom kedua, array warna di kolom ketiga, dan seterusnya. Data untuk baris ke-i bisa

didapatkan dari masing-masing array ini. Tidak ada yang salah dengan cara ini, akan tetapi

cara ini berlawanan dengan filosofi berorientasi objek yang mengumpulkan data yang

berhubungan di dalam satu objek. Jika kita mengikuti aturan seperti ini, amaka kita tidak

harus membayangkan hubungan data yang satu dan yang lainnya karena semua data akan

dikelompokkan di dalam satu tempat.

Misalnya saya menulis kelas seperti

class DataString {

// Data dari salah satu pesan

int x,y; // Posisi pesan

Color warna; // Warna pesan

}

Untuk menyimpan data dalam beberapa pesan, kita bisa menggunakan array bertipe

DataString[], yang kemudian dideklarasikan sebagai variabel instansi dengan nama data

sehingga

Page 197: Java With Eclipse

DataString[] data;

Isi dari data bernilai null hingga kita membuat array baru, misalnya dengan

data = new DataString[JUMLAH_PESAN];

Setelah array ini dibuat, nilai setiap elemen array adalah null. Kita ingin menyimpan data

di dalam objek yang bertipe DataString, akan tetapi tidak ada objek yang dibuat. Yang

kita sudah buat hanyalah kontainernya saja. Elemen di dalamnya berupa objek yang belum

pernah kita buat. Untuk itu elemen di dalamnya bisa kita buat dengan perulangan for

seperti :

for (int i = 0; i < JUMLAH_PESAN; i++)

data[i] = new DataString();

Sekarang kita bisa mengambil data setiap pesan dengan data[i].x, data[i].y, dan

data[i].warna.

Terakhir berkaitan dengan pernyataan switch. Misalnya kita memiliki nilai bulan dari 0

hingga 11, yang melambangkan bulan dalam satu tahun dari Januari hingga Desember.

Kita ingin mencetaknya di layar, dengan perintah

switch (bulan) {

case 0:

bulanString = "Januari";

break;

case 1:

bulanString = "Februari";

break;

case 2:

bulanString = "Maret";

break;

case 3:

bulanString = "April";

break;

.

.

.

case 11:

bulanString = "Desember";

break;

default:

bulanString = "Salah bulan";

}

Page 198: Java With Eclipse

Kita bisa mengganti keseluruhan perintah switch tersebut dengan menggunakan array,

misalnya dengan array namaBulan yang dideklarasikan sebagai berikut :

static String[] namaBulan = { "Januari", "Februari", "Maret",

"April", "Mei", "Juni", "Juli", "Agustus", "September",

"Oktober", "November", "Desember" };

Kemudian kita bisa ganti keseluruhan switch di atas dengan

bulanString = namaBulan[bulan];

Sangat mudah bukan?

Array Dinamis

Panjang suatu array tidak bisa diubah setelah dibuat. Akan tetapi, sering kali jumlah data

yang disimpan dalam array bisa berubah setiap saat. Misalnya dalam contoh berikut : Suatu

array menyimpan baris teks dalam program pengolah kata. Atau suatu array yang

menyimpan daftar komputer yang sedang mengakses halaman suatu website. Atau array

yang berisi gambar yang ditambahkan user oleh program gambar. Di sini jelas, bahwa kita

butuh sesuatu yang bisa menyimpan data di mana jumlahnya tidak tetap.

Array Setengah Penuh

Bayangkan suatu aplikasi di mana sejumlah item yang ingin kita simpan di dalam array

akan berubah-ubah sepanjang program tersebut berjalan. Karena ukuran array tidak bisa

diubah, maka variabel terpisah digunakan untuk menyimpan berapa banyak sisa tempat

kosong yang masih bisa diisi di dalam array.

Bayangkan misalnya, suatu program yang membaca bilangan bulat positif dari user,

kemudian menyimpannya untuk diproses kemudian. Program akan berhenti membaca

input apabila input yang diterima bernilai 0 atau kurang dari nol. Bilangan input n tersebut

kita simpa di dalam array bilangan dengan tipe int[]. Katakan banyaknya bilangan yang

bisa disimpan tidak lebih dari 100 buah. Maka ukuran array bisa dibuat 100.

Akan tetapi program tersebut harus melacak berapa banyak bilangan yang sudah diambil

dari user. Kita gunakan variabel terpisah bernama jmlBilangan. Setiap kali suatu bilangan

disimpan di dalam array, nilai jmlBilangan akan bertambah satu.

Sebagai contoh sederhana, masi kita buat program yang mengambil bilangan yang diinput

dari user, kemudian mencetak bilangan-bilangan tersebut dalam urutan terbalik. (Ini adalah

contoh pengolahan yang membutuhkan array, karena semua bilangan harus disimpan pada

suatu tempat. Banyak lagi contoh program misalnya, mencari jumlah atau rata-rata atau

nilai maksimum beberapa bilangan, bisa dilakukan tanpa menyimpan bilangan tersebut

satu per satu)

Page 199: Java With Eclipse

public class BalikBilanganInput {

public static void main(String[] args) {

int[] bilangan; // Array untuk menyimpan nilai input dari user

int jmlBilangan; // Banyaknya bilangan yang sudah disimpan

dalam array

int bil; // Bilangan yang diambil dari user

bilangan = new int[100]; // Buat array dengan 100 bilangan int

jmlBilangan = 0; // Belum ada bilangan yang

disimpan

System.out.println("Masukkan bilangan bulat positif (paling

banyak 100 bilangan)" +

", masukkan nol untuk mengakhiri.");

while (true) {

System.out.print("? ");

bil = KonsolIO.ambilInt();

if (bil <= 0)

break;

bilangan[jmlBilangan] = bil;

jmlBilangan++;

}

System.out.println("\nBilangan yang Anda masukkan dalam urutan

terbalik adalah :\n");

for (int i = jmlBilangan - 1; i >= 0; i--) {

System.out.println( bilangan[i] );

}

} // akhir main();

} // akhir kelas BalikBilanganInput

Penting untuk diingat bahwa jmlBilangan memiliki dua peran. Yang pertama, adalah

melacak banyaknya bilangan yang sudah dimasukkan di dalam array. Yang kedua adalah

menentukan di mana indeks kosong berikutnya di dalam array. Misalnya, jika 4 bilangan

Page 200: Java With Eclipse

sudah dimasukkan ke dalam array, maka bilangan-bilangan tersebut diisi pada array di

posisi 0, 1, 2, dan 3. Maka posisi kosong berikutnya adalah posisi 4.

Ketika kita akan mencetak angka di dalam array, maka poisisi penuh berikutnya adalah di

lokasi jmlBilangan - 1, sehingga perulangan for mencetak bilangan dari jmlBilangan

- 1 hingga 0.

Mari kita lihat contoh lain yang lebih realistis. Misalkan kita ingin menulis program game,

di mana pemain bisa masuk ke dalam game dan keluar dari game setiap saat. Sebagai

programmer berorientasi objek yang baik, mungkin kita memiliki kelas bernama Pemain

sebagai lambang pemain di dalam game. Daftar pemain yang sedang ada di dalam game,

bisa disimpan di dalam array ArrayPemain dengan tipe Pemain[].

Karena jumlah pemain bisa berubah-ubah maka kita bisa menggunakan variabel bantu,

misalnya jumlahPemainAktif untuk mencatat banyaknya pemain yang sedang aktif

berada di dalam game. Misalnya jumlah maksimum pemain di dalam game adalah 10

orang, maka kita bisa mendeklarasikan variabelnya sebagai :

Pemain[] ArrayPemain = new Pemain[10]; // Hingga 10 pemain.

int jumlahPemainAktif = 0; // Di awal game, tidak ada pemain yang aktif

Setelah beberapa pemain masuk ke dalam game, variabel jumlahPemainAktif akan lebih

dari 0, dan objek pemailn akan disimpan dalam array, misalnya ArrayPemain[0],

ArrayPemain[1], ArrayPemain[2], dan seterusnya. Ingat bahwa pemain

ArrayPemain[jumlahPemainAktif] tidak ada. Prosedur untuk menambah pemain baru,

secara sederhana :

// Tambah pemain di tempat kosong

ArrayPemain[jumlahPemainAktif] = pemainBaru;

// And increment playerCt to count the new player.

jumlahPemainAktif++;

Untuk menghapus seorang pemain mungkin sedikit lebih sulit karena kita tidak ingin

meninggalkan lubang di tengah-tengah array. Misalnya kita ingin menghapus pemain pada

indeks k pada ArrayPemain. Jika kita tidak peduli urutan pemainnya, maka salah satu

caranya adalah memindahkan posisi pemain terakhir ke posisi pemain yang meninggalkan

game, misalnya :

ArrayPemain[k] = ArrayPemain[jumlahPemainAktif - 1];

jumlahPemainAktif--;

Pemain yang sebelumnya ada di posisi k, tidak lagi ada di dalam array. Pemain yang

sebelumnya ada di posisi jumlahPemainAktif -1 sekarang ada di array sebanyak 2 kali.

Akan tetapi sekarang ia berada di bagian yang valid, karena nilai jumlahPemainAktif kita

Page 201: Java With Eclipse

kurangi dengan satu. Ingat bahwa setiap elemen di dalam array harus menyimpan satu

nilai, akan tetapi satu-satunya nilai dari posisi 0 hingga jumlahPemainAktif - 1 akan

tetap diproses seperti biasa.

Misalnya kita ingin menghapus pemain di posisi k, akan tetapi kita ingin agar urutan

pemain tetap sama. Untuk melakukannya, semua pemain di posisi k+1 ke atas harus

dipindahkan satu posisi ke bawah. Pemain k+ mengganti pemain k yang keluar dari game.

Pemain k+2 mengganti pemain yang pindah sebelumnya, dan berikutnya. Kodenya bisa

dibuat seperti

for (int i = k+1; i < jumlahPemainAktif; i++) {

ArrayPemain[i-1] = ArrayPemain[i];

}

jumlahPemainAktif--;

Perlu ditekankan bahwa contoh Pemain di atas memiliki tipe dasar suatu kelas. Elemennya

bisa bernilai null atau referensi ke suatu objek yang bertipe Pemain, Objek Pemain sendiri

tidak disimpan di dalam array, hanya referensinya saja yang disimpan di sana. Karena

aturan pemberian nilai pada Java, objek tersebut bisa saja berupa kelas turunan dari

Pemain, sehingga mungkin juga array tersebut menyimpan beberapa jenis Pemain

misalnya pemain komputer, pemain manusia, atau pemain lainnya, yang semuanya adalah

kelas turunan dari Pemain.

Contoh lainnya, misalnya kelas BentukGeometri menggambarkan suatu bentuk geometri

yang bisa digambar pada layar, dan ia memiliki kelas-kelas turunan yang merupakan

bentuk-bentuk khusus, seperti garis, kotak, kotak bertepi bulat, oval, atau oval berisi

warna, dan sebagainya. (BentukGeometri sendiri bisa berbentuk kelas abstrak, seperti

didiskusikan sebelumnya). Kemudian array bertipe BentukGeometri[] bisa menyimpan

referensi objek yang bertipe kelas turunan dari BentukGeometri. Misalnya, perhatikan

contoh pernyataan berikut

BentukGeometri[] gambar = new BentukGeometri[100]; // Array untuk

menyimpan 100 gambar.

gambar[0] = new Kotak(); // Letakkan beberapa objek di dalam

array.

gambar[1] = new Garis(); // (Program betulan akan menggunakan

beberapa

gambar[2] = new OvalBerwarna(); // parameter di sini

int jmlGambar = 3; // Lacak jumlah objek di dalam array

bisa diilustrasikan sebagai berikut.

Page 202: Java With Eclipse

Array tersebut bisa digunakan dalam program gambar. Array bisa digunakan untuk

menampung gambar-gambar yang akan ditampilkan. Jika BentukGeometri memiliki

metode "void gambarUlang(Graphics g)" untuk menggambar pada grafik g, maka

semua grafik dalam array bisa digambar dengan perulangan sederhana

for (int i = 0; i < jmlGambar; i++)

gambar[i].gambarUlang(g);

Pernyataan "gambar[i].gambarUlang(g);" memanggil metode gambarUlang() yang

dimiliki oleh masing-masing gambar pada indeks i di array tersebut. Setiap objek tahu

bagaimana menggambar dirinya sendiri, sehingga perintah dalam perulangan tersebut

sebetulnya melakukan tugas yang berbeda-beda tergantung pada objeknya. Ini adalah

contoh dari polimorfisme dan pengolahan array.

Array Dinamis

Dalam contoh-contoh di atas, ada batas tententu dalam jumlah elemennya, yaitu 100 int,

100 Pemain, dan 100 BentukGeometris. Karena ukuran array tidak bisa berubah, array

tersebut hanya bisa menampung maksimum sebanyak elemen yang didefinisikan pada

pembuatan array. Dalam banyak kasus, adanya batas maksimum tersebut tidak diinginkan.

Kenapa harus bekerja dengan hanya 100 bilangan bulat saja, bukan 101?

Alternatif yang umum adalah membuat array yang sangat besar sehingga bisa digunakan

untuk dalam kehidupan sehari-hari. Akan tetapi cara ini tidak baik, karena akan sangat

banyak memori komputer yang terbuang karena tidak digunakan. Memori itu mungkin

lebih baik digunakan untuk yang lain. Apalagi jika komputer yang akan digunakan tidak

memiliki memori yang cukup untuk menjalankan program tersebut.

Page 203: Java With Eclipse

Tentu saja, cara yang lebih baik adalah apabila kita bisa mengubah ukuran array sesuka

kita kapan saja. Ingat bahwa sebenarnya variabel array tidak menyimpan array yang

sesungguhnya. Variabel ini hanya menyimpan referensi ke objek tersebut. Kita tidak bisa

membuat array tersebut lebih besar, akan tetapi kita bisa membuat array baru yang lebih

besar, kemudian mengubah isi variabel array tersebut ke array baru itu.

Tentunya kita harus mengkopi semua isi di array yang lama ke array baru. Array lama akan

diambil oleh pemulung memori, karena ia tidak lagi digunakan.

Mari kita lihat kembali contoh game di atas, di mana ArrayPemain adalah array dengan

tipe Pemain[] dan jumlahPemainAktif[/code] adalah jumlah pemain yang sudah

digunakan array tersebut. Misalnya kita tidak ingin membuat limit banyaknya pemainnya

yang bisa ikut main. Jika pemain baru masuk dan array tersebut sudah penuh, kita akan

membuat array baru yang lebih besar.

Variabel ArrayPemain akan merujuk pada array baru. Ingat bahwa setelah ini dilakukan,

ArrayPemain[0] akan menunjuk pada lokasi memori yang berbeda, akan tetapi nilai

ArrayPemain[0] sama dengan sebelumnya. Berikut ini adalah kode untuk melakukan hal

di atas:

// Tambah pemain baru, meskipun array sudah penuh

if (jumlahPemainAktif == ArrayPemain.length) {

// Array sudah penuh. Buat array baru yang lebih besar,

// kemudian kopi isi array lama ke array baru lalu ubah

// ArrayPemain ke array baru.

int ukuranBaru = 2 * ArrayPemain.length; // Ukuran array baru

Pemain[] temp = new Pemain[ukuranBaru]; // Array baru

System.arraycopy(ArrayPemain, 0, temp, 0, ArrayPemain.length);

ArrayPemain = temp; // Ubah referensi ArrayPemain ke array baru.

}

// Di sini kita sudah tahu bahwa pasti ada tempat di array baru.

ArrayPemain[jumlahPemainAktif] = pemainBaru; // Tambah pemain baru...

jumlahPemainAktif++; // ... dan tambah satu jumlahPemainAktif nya

Jika kita akan melakukan hal ini terus menerus, akan lebih indah jika kita membuat kelas

untuk menangani hal ini. Objek mirip array yang bisa berubah ukuran untuk

mengakomodasi jumlah data yang bisa ia tampung disebut array dinamis. Array dinamis

memiliki jenis operasi yang sama dengan array : mengisi nilai pada posisi tertentu dan

Page 204: Java With Eclipse

mengambil nilai di posisi tertentu. Akan tetapi tidak ada batas maksimum dari jumlah array

(hanya tergantung pada jumlah memori komputer yang tersedia). Dalam kelas array

dinamis, metode put dan get akan diimplementasikan sebagai metode instansi.

Di sini misalnya, adalah kelas yang mengimplementasikan array dinamis int :

public class ArrayDinamisInt {

private int[] data; // Array untuk menyimpan data

public DynamicArrayOfInt() {

// Konstruktor.

data = new int[1]; // Array akan bertambah besar jika diperlukan

}

public int get(int posisi) {

// Ambil nilai dari posisi tertentu di dalam array.

// Karena semua posisi di dalam array adalah nol, maka

// jika posisi tertentu di luar data array, nilai 0 akan

dikembalikan

if (posisi >= data.length)

return 0;

else

return data[posisi];

}

public void put(int posisi, int nilai) {

// Simpan nilai ke posisi yang ditentukan di dalam array

// Data array tersebut akan bertambah besar jika diperlukan

if (posisi >= data.length) {

// Posisi yang ditentukan berada di luar array data

// Besarkan ukuran array 2x lipat. Atau jika ukurannya masih

// terlalu kecil, buat ukurannya sebesar 2*posisi

int ukuranBaru = 2 * data.length;

if (posisi >= ukuranBaru)

ukuranBaru = 2 * posisi;

int[] dataBaru = new int[ukuranBaru];

System.arraycopy(data, 0, dataBaru, 0, data.length);

data = dataBaru;

// Perintah berikut hanya untuk demonstrasi

System.out.println("Ukuran array dinamis diperbesar menjadi "

Page 205: Java With Eclipse

+ ukuranBaru);

}

data[posisi] = nilai;

}

} // akhir kelas ArrayDinamisInt

Data pada objek ArrayDinamisInt disimpan dalam array biasa, akan tetapi arraynya akan

dibuang dan diganti dengan array baru yang lebih besar apabila diperlukan. Jika bilangan

adalah variable bertipe ArrayDinamisInt, maka perintah bilangan.put(pos,nilai)

akan menyimpan bilangan pada posisi pos di array dinamis tersebut. Fungsi

bilangan.get(pos) mengambil nilai yang disimpan pada posisi pos.

Pada contoh pertama, kita menggunakan array untuk menyimpan bilangan bulat positif

yang dimasukkan oleh user. Kita bisa menulis ulang program tersebut dengan

menggunakan ArrayDinamisInt. Referensi ke bilangan[i] diganti dengan

bilangan.get[i]. Perintah "bilangan[jmlBilangan] = bil;" kita ganti dengan

"bilangan.put(jmlBilangan,bil);". Berikut ini adalah programnya:

public class BalikBilanganInput {

public static void main(String[] args) {

ArrayDinamisInt bilangan; // Array untuk menyimpan nilai input

dari user

int jmlBilangan; // Banyaknya bilangan yang sudah disimpan

dalam array

int bil; // Bilangan yang diambil dari user

bilangan = new ArrayDinamisInt();

jmlBilangan = 0; // Belum ada bilangan yang

disimpan

System.out.println("Masukkan bilangan bulat positif, masukkan nol

untuk mengakhiri.");

while (true) {

System.out.print("? ");

bil = KonsolIO.ambilInt();

if (bil <= 0)

break;

bilangan.put(jmlBilangan,bil);

Page 206: Java With Eclipse

jmlBilangan++;

}

System.out.println("\nBilangan yang Anda masukkan dalam urutan

terbalik adalah :\n");

for (int i = jmlBilangan - 1; i >= 0; i--) {

System.out.println( bilangan.get(i) );

}

} // akhir main();

} // akhir kelas BalikBilanganInput

ArrayLists dan Vector

ArrayList

Kelas ArrayDinamisInt bisa digunakan jika kita membutuhkan array dengan batas

maksimum yang tak terbatas. Akan tetapi jika kita ingin menyimpan BentukGeometri

misalnya, kita harus membuat kelas baru. Kelas tersebut mungkin bernama

ArrayDinamisBentukGeometri, dan akan tampak sama persis dengan ArrayDinamisInt

dengan beberapa perubahan di mana "int" muncul akan diganti dengan

"BentukGeometri". Juga demikian jika kita ingin membuat kelas ArrayDinamisDouble,

ArrayDinamisPemain, dan sebagainya.

Yang agak sedikit lucu adalah kelas tersebut sangat mirip isinya, hanya kelas-kelasnya saja

yang berlainan. Akan sangat lebih baik apabila kita bisa membuat kelas umum yang

berlaku untuk semua kelas. Hal ini disebut dengan pemrograman generik. Beberapa bahasa

pemrograman seperti C++ memiliki fasilitas untuk melakukan pemrograman generik. Java

tidak memiliki fasilitas seperti itu (maksudnya tidak sama persis). Tapi kita bisa membuat

sesuatu yang mirip dengan itu pada Java dengan menggunakan struktur data yang memiliki

tipe "Object".

Pada Java, setiap kelas adalah kelas turunan dari kelas yang bernama Object. Artinya

setiap objek dapat dirujuk oleh variabel bertiipe Object. Semua objek dapat dimasukkan

dalam array bertipe Object[]. Jika suatu subrutin memiliki parameter formal bertipe

Object, maka objek apapun bisa diberikan kepada subrutin tersebut.

Jika kita buat kelas ArrayDinamisObject, maka kita bisa menyimpan objek apapun. Ini

sebenarnya bukan programming generik sebenarnya, dan tidak berlaku untuk tipe primitif

Page 207: Java With Eclipse

seperti int dan double. Sebenarnya, kita tidak perlu membuat kelas ArrayDinamisObject

sendiri, karena Java telah memiliki kelas standar yang dinamakan ArrayList yang sifatnya

mirip dengan yang kita sebutkan sebelumnya.

ArrayList disimpan dalam paket java.util, jadi jika kita ingin menggunakan kelas

ArrayList dalam sebuat program, kita harus memberikan "import

java.util.ArrayList;" atau "import java.util.*;" di awal program kita.

Kelas ArrayList berbeda dengan kelas ArrayDinamisInt dalam arti, objek ArrayList

selalu memiliki ukuran tertentu, dan kita tidak boleh mengambil posisi di luar ukuran

ArrayList. Dalam hal ini, ArrayList mirip seperti array biasa. Akan tetapi, ukuran

ArrayList bisa bertambah kapan saja jika diperlukan. Kelas ArrayList memiliki banyak

metode instansi. Kita akan jelaskan sebagian yang sangat berguna. Misalnya daftar

adalah variabel dengan tipe ArrayList.

daftar.size() -- Fungsi ini menghasilkan ukuran ArrayList saat ini. Posisi valid

dalam daftar adalah yang bernomor 0 hingga daftar.size() - 1. Ingat bahwa

ukurannya bisa nol. Panggilan konstruktor new ArrayList() akan membuat

ArrayList dengan ukuran nol.

daftar.add(obj) -- Menambah objek di akhir ArrayList, kemudian menambah

ukurannya dengan 1. Parameter obj merujuk pada objek dengan tipe apapun, atau

bisa bernilai null.

daftar.get(N) -- Mengambil nilai yang disimpan pada posisi N pada ArrayList.

N harus bertipe bilangan bulat antara 0 hingga daftar.size() - 1. Jika N berada

di luar rentang ini, pesan kesalahan akan ditampilkan. Memanggil fungsi ini mirip

dengan memanggil array seperti A[N] untuk suatu array A. Akan tetapi kita tidak

bisa menggunakan daftar.get(N) pada ekspresi di sebelah kiri daftar.get(N) =

5 tidak boleh dilakukan)

daftar.set(N, obj) -- Mengisi suatu objek obj di posisi N dari suatu ArrayList,

dan mengganti apapun yang sudah disimpan sebelumnya pada posisi N. Bilangan

bulat N harus berada pada rentang 0 hingga daftar.size() - 1. Perintah ini mirip

dengan perintah A[N] = obj pada array A.

daftar.remove(obj) -- Jika objek tersebut ada di dalam ArrayList, maka objek

tersebut akan dibuang. Elemen lain setelah posisi objek tersebut di dalam

ArrayList akan digeser satu posisi. Kemudian ukuran ArrayList akan berkurang

Page 208: Java With Eclipse

1. Jika obj terdapat dalam ArrayList lebih dari satu kali, hanya yang pertama saja

yang dibuang.

daftar.remove(N) -- Untuk bilangan bulat N, maka perintah ini akan menghapus

posisi ke-N pada ArrayList. N harus berada pada rentang 0 hingga

daftar.size() - 1. Elemen setelah ini akan digeser satu posisi. Kemudian

ukuran ArrayList berkurang 1.

daftar.indexOf(obj) -- Fungsi untuk mencari suatu objek obj di dalam

ArrayList. Jika objek ditemukan, maka posisinya di dalam ArrayList akan

dikembalikan. Jika tidak, maka fungsi ini akan mengembalikan -1.

Misalnya,kita akan membuat pemain dalam game yang digambarkan dengan objek dengan

tipe Pemain. Pemain yang berada dalam game disimpan dalam ArrayList bernama

paraPemain. Variabel ini dideklarasikan seperti :

ArrayList paraPemain;

dan kita inisialisasikan dengan objek kosong dengan perintah

paraPemain = new ArrayList();

Jika pemainBaru adalah variabel yang bertipe Pemain, maka pemain baru tersebut bisa

ditambahkan ke dalam ArrayList dan ke dalam game dengan perintah :

paraPemain.add(pemainBaru);

dan jika pemain nomor i keluar dari game, maka kita hanya perlu memberikan perintah

paraPemain.remove(i);

Atau jika pemain adalah objek bertipe Pemain yang akan kita keluarkan dari game, maka

kita bisa menggunakan perintah

paraPemain.remove(pemain);

Semuanya terlihat sangat mudah. Satu-satunya kesulitan yang akan kita temui adalah

ketika kita ingin mengambil nilai yang disimpan pada posisi i di dalam ArrayList. Tipe

keluaran fungsi ini adalah Object. Dalam hal ini objek yang diambil oleh fungsi ini

sebenarnya bertipe Pemain. Supaya kita bisa menggunakan hasil keluarannya, maka kita

perlu menggunakan casting tipe untuk mengubahnya menjadi tipe Player, dengan cara :

Pemain pmn = (Pemain)paraPemain.get(i);

Misalnya, jika kelas Pemain memiliki metode instansi yang dinamakan jalan() yang

dipanggil ketika seorang pemain menjalankan dadu atau kartu, maka kita bisa menuliskan

kode untuk memberi giliran kepada semua pemain untuk jalan, yaitu dengan

for (int i = 0; i < paraPemain.size(); i++) {

Pemain pmn = (Pemain)paraPemain.get(i);

pmn.jalan();

Page 209: Java With Eclipse

}

Dua baris di dalam perulangan for tersebut dapat digabungkan dengan satu perintah :

((Pemain)paraPemain.get(i)).jalan();

Perintah ini akan mengambil elemen pada ArrayList mengcasting tipenya, kemudian

memanggil metode jalan() pada pemain yang baru diambil tersebut. Tanda kurung di

sekitar "(Pemain)paraPemain.get(i)" diperlukan karena aturan dalam Java sehingga

perintah dalam kurung akan dijalankan terlebih dahulu sebelum metode jalan()

dipanggil.

Vector

Kelas ArrayList diperkenalkan pada Java versi 1.2, sebagai salah satu kumpulan kelas

yang digunakan untuk bekerja dengan sekumpulan koleksi objek. Jika akan bahas lebih

lanjut tentang "kelas koleksi" pada bagian berikutnya. Versi awal Java tidak memiliki

ArrayList, akan tetapi memiliki kelas yang sangat mirip yaitu java.util.Vector. Kita

masih bisa melihat Vector digunakan pada program lama, dan dalam beberapa kelas

standar Java, sehingga kita perlu tahu tentang kelas ini.

Menggunakan Vector mirip dengan menggunakan ArrayList. Perbedaannya adalah nama

metode yang berbeda untuk melakukan tugas yang sama, atau nama metode yang berbeda

untuk melakukan tugas yang sama.

Seperti ArrayList, suatu Vector mirip dengan array Object yang bisa berkembang jika

diperlukan. Konstruktor new Vector() membuat vektor tanpa elemen.

Misalnya vec adalah suatu Vector. Maka :

vec.size() adalah fungsi untuk mengembalikan jumlah elemen di dalam vektor.

vec.addElement(obj) akan menambahkan Object obj di akhir vektor. Sama

dengan metode add() pada ArrayList.

vec.removeElement(obj) menghapus obj dari dalam vektor, kalau ada. Hanya

objek pertama yang ditemui akan dihapus. Sama dengan remove(obj) pada kelas

ArrayList

vec.removeElementAt(N) menghapus elemen ke-N. N harus berada pada rentang

0 hingga vec.size() - 1. Sama dengan remove(N) pada ArrayList

vec.setSize(N) akan mengubah ukuran vektor menjadi N. Jika di dalam vektor

terdapat elemen yang jumlahnya lebih banyak dari N, maka elemen lainnya akan

dihapus. Jika lebih sedikit, maka tempat kosong akan diisi dengan null. Kelas

ArrayList tidak memiliki metode seperti ini.

Page 210: Java With Eclipse

Kelas Vector memiiki banyak metode lagi, akan tetapi ini adalah metode yang sering

digunakan.

Array Multi Dimensi

Tipe apapun bisa digunakan sebagai tipe dasar suatu array. Kita bisa membuat array int,

array String, array Object dan seterusnya. Terutama, karena array adalah tipe Java kelas

satu, kita bisa membuat array yang bertipe array.

Misalnya suatu array bertipe int[], juga otomatis memiliki array bertipe int[][], yaitu

"array bertipe array int". Array tersebut disebut array 2 dimensi. Tentunya, dengan tipe

int[][], kita juag bisa membuat arraynya dengan tipe int[][][], yang merupakan array

3 dimensi, dan seterusnya. Tidak ada batasan berapa dimensi array yang kita buat, akan

tetapi bukan sesuatu yang biasa dilakukan untuk membuat array lebih dari 3 dimensi.

Pembahasan kita akan lebih dikhususkan pada array 2 dimensi. Tipe TipeDasar[][]

biasanya dibaca "array 2 dimensi bertipe TipeDasar" atau "array dari array TipeDasar".

Deklarasi pernyataan "int[][] A;" adalah membuat variabel bernama A dengan tipe

int[][]. Variabel ini berisi objek yang bertipe int[][]. Pernyataan pemberian nilai "A =

new int[3][4];" akan membuat objek array 2 dimensi dan mengisi A ke objek yang baru

dibuat tersebut.

Seperti biasa, deklarasi dan pemberian nilai bisa digabung menjadi satu pernyataan, seperti

"int[][] A = new int[3][4];". Objek yang baru dibuat adalah objek yang merupakan

array dari array int. Bagian int[3][4] menyatakan bahwa ada 3 array int di dalam array

A, dan di setiap array int tersebut terdapat 4 int.

Cara seperti itu mungkin sedikit membingungkan, akan tetapi akan lebih mudah apabila

kita bayangkan array tersebut seperti matriks. Istilah "int[3][4]" bisa disebut sebagai

matriks dengan 3 baris dan 4 kolom, seperti pada ilustrasi berikut ini :

Page 211: Java With Eclipse

Untuk banyak hal, kita bisa mengabaikan kenyataan di atas, dan membayangkan bentuk

matriks seperti di atas. Kadang-kadang kita juga harus ingat bahwa setiap baris sebenarnya

juga merupakan suatu array. Array-array ini bisa dirujuk dengan A[0], A[1], dan A[2].

Setiap baris bertipe int[].

Pernyataan A[1] merujuk pada salah satu baris pada array A. Karena A[1] itu sendiri

sebenarnya adalah array int, kita bisa menambah indeks lain untuk merujuk pada posisi

pada baris tersebut. Misalnya A[1][3] adalah elemen nomor 3 pada baris 1. Seperti biasa,

ingat bahwa posisi baris dan kolom dimulai dari 0. Jadi pada contoh di atas, A[1][3]

bernilai 5. Lebih umum lagi, A[i][j] adalah posisi pada baris i dan kolom j. Seluruh

elemen pada A bisa dinamakan seperti berikut :

A[0][0] A[0][1] A[0][2] A[0][3]

A[1][0] A[1][1] A[1][2] A[1][3]

A[2][0] A[2][1] A[2][2] A[2][3]

A[i][j] adalah variabel bertipe int. Kita bisa mengisi nilainya atau menggunakannya

seperti variabel bertipe int biasa.

Page 212: Java With Eclipse

Perlu juga diketahui bahwa A.length akan memberikan jumlah baris pada A. Untuk

mendapatkan jumlah kolom pada A, kita harus mencari jumlah int dalam setiap baris, yaitu

yang disimpan pada A[0]. Jumlah kolom ini bisa didapatkan dengan menggunakan

A[0].length, atau A[1].length atau A[2].length. (Tidak ada aturan baku yang

menyatakan bahwa pada setiap baris suatu array harus memiliki panjang yang sama, dan

sebenarnya pada beberapa aplikasi, juga digunakan array dengan panjang yang berbeda-

beda pada setiap barisnya. Akan tetapi apabila kita membuat array dengan perintah seperti

di atas, maka kita akan selalu mendapatkan array dengan panjang array yang sama.)

Array 3 dimensi juga dibuat dan diolah dengan cara yang sama. Misalnya, array 3 dimensi

bertipe int bisa dibuat dengan pernyataan "int[][][] B = new int [7][5][11];". Kita

juga bisa mengilustrasikannya sebagai kubus 3-dimensi. Masing-masing bloknya bertipe

int yang bisa dipanggil dalam bentuk B[i][j][k]. Array dimensi lain yang lebih tinggi

juga mengikuti pola yang sama, akan tetapi akan sangat sulit untuk membuat visualisasi

struktur arraynya.

Kita bisa mengisi array multi dimensi sekaligus pada saat dideklarasikan. Ingat

sebelumnya bagaimana array 1 dimensi biasa dideklarasikan, dan bagaimana isinya

diinisialisasikan, yaitu seperti daftar nilai-nilainya yang dipisahkan dengan koma, dan

diletakkan di dalam tanda kurung kurawal { dan }.

Inisialisasi array bisa juga digunakan untuk array multi dimensi, yang terdiri dari beberapa

inisialisasi array 1 dimensi, masing-masing untuk setiap barisnya. Misalnya, array A pada

gambar di atas dapat dibuat dengan perintah :

int[][] A = { { 1, 0, 12, -1 },

{ 7, -3, 2, 5 },

{ -5, -2, 2, 9 }

};

Jika tidak ada inisialisasi yang diberikan untuk suatu array, maka nilainya akan diisi

dengan nilai awal tergantung pada tipenya : nol untuk bilangan, false untuk boolean dan

null untuk objek.

Seperti halnya array 1 dimensi, array 2 dimensi juga sering diolah dengan menggunakan

perulangan for. UNtuk mengolah semua elemen pada array 2 dimensi, kita bisa

menggunakan pernyataan for bertingkat. Jika array A dideklarasikan seperti

int[][] A = new int[3][4];

Page 213: Java With Eclipse

maka kita bisa mengisi 0 untuk semua elemen pada A dengan menggunakan

for (int baris = 0; baris < 3; baris++) {

for (int kolom = 0; kolom < 4; kolom++) {

A[baris][kolom] = 0;

}

}

Pertama kali perulangan for bagian luar akan memproses dengan baris = 0. Bagian

dalamnya akan mengisi keempat kolom pada baris pertama, yaitu A[0][0] = 0, A[0][1]

= 0, A[0][2] = 0, dan A[0][3] = 0. Kemudian perulangan for bagian luar akan mengisi

baris kedua, dan seterusnya.

Dan juga, kita bisa menjumlah semua elemen pada A dengan

int jml = 0;

for (int i = 0; i < 3; i++)

for (int j = 0; j < 4; i++)

jml = jml + A[i][j];

Untuk mengolah array 3 dimensi, tentunya kita harus menggunakan perulangan for

bertingkat 3.

Suatu array 2 dimensi bisa digunakan untuk menyimpan data yang secara alami memang

tersusun sebagai baris dan kolom. Misalnya papan catur terdiri dari 8 baris dan 8 kolom.

Jika suatu kelas dinamakan PapanCatur untuk merepresentasikan papan catur, maka kita

bisa deklarasikan dengan perintah

PapanCatur[][] papan = new PapanCatur[8][8];

Kadang-kadang array 2 dimensi juga digunakan untuk masalah yang tidak terlalu jelas

matriksnya. Misalnya perusahaan yang memiliki 25 toko. Anggap masing-masing toko

memiliki keuntungan yang didapat pada masing-masing toko tersebut setiap bulan pada

tahun 2009. Jika toko-toko tersebut memiliki nomor 0 hingga 24, dan 12 bulan dari Januari

09 hingga Desember 09 dinomori 0 hingga 11, maka data keuntungan dapat disimpan

dalam array untung yang dideklarasikan seperti :

double[][] untung = new double[25][12];

untung[3][2] adalah keuntungan yang dibuat oleh toko nomor 3 di bulan Maret. Atau

secara umum, untung[noToko][noBulan] adalah keuntungan toko noToko pada bulan

noBulan. Dalam contoh ini array 1 dimensi untung[noToko] memiliki arti : Data

keuntungan satu toko selama satu tahun.

Page 214: Java With Eclipse

Anggap array untung telah diisi dengan data. Data ini bisa diolah lebih lanjut. Misalnya,

total keuntungan seluruh perusahaan -- sepanjang tahun dari seluruh toko -- dapat dihitung

dengan menjumlahkan semua elemen pada array :

double totalUntung; // Total keuntungan perusahaan tahun 2009

totalUntung = 0;

for (int toko = 0; toko < 25; toko++) {

for (int bulan = 0; bulan < 12; bulan++)

totalUntung += untung[toko][bulan];

}

Kadang-kadang kita juga perlu menghitung hanya satu baris atau satu kolom saja, bukan

keseluruhan array. Misalnya, kita ingin menghitung keuntungan total perusahaan pada

bulan Desember, yaitu bulan nomor 11, maka kita bisa gunakan perulangan :

double untungDesember = 0.0;

for (noToko = 0; noToko < 25; noToko++)

untungDesember += untung[noToko][11];

Sekarang mari kita buat array 1 dimensi yang berisi total keuntungan seluruh toko setiap

bulan :

double[] untungBulanan; // Keuntungan setiap bulan

untungBulanan = new double[12];

for (int bulan = 0; bulan < 12; bulan++) {

// hitung total keuntungan semua toko bulan ini

untungBulanan[bulan] = 0.0;

for (int toko = 0; toko < 25; toko++) {

untungBulanan[bulan] += profit[toko][bulan];

}

}

Sebagai contoh terakhir untuk mengolah array keuntungan, misalnya kita ingin tahu toko

mana yang menghasilkan keuntungan terbesar sepanjang tahun. Untuk menghitungnya,

kita harus menjumlahkan keuntungan setiap toko sepanjang tahun. Dalam istilah array, ini

berarti kita ingin mengetahui jumlah setiap baris pada array. Kita perlu mencatat hasil

perhitungannya untuk mencari mana toko dengan keuntungan terbesar.

double untungMaks; // Keuntungan terbesar suatu toko

int tokoTerbaik; // Nomor toko yang memiliki keuntungan terbesar

double total = 0.0; // Total keuntungan suatu toko

Page 215: Java With Eclipse

// Pertama-tama hitung keuntungan dari toko nomo 0

for (int bulan = 0; bulan < 12; bulan++)

total += untung[0][bulan];

tokoTerbaik = 0; // Mulai dengan anggapan toko nomor 0

untungMaks = total; // adalah toko paling menguntungkan

// Sekarang kita lihat seluruh toko, dan setiap kali

// kita melihat toko dengan keuntungan lebih besar dari

// untungMaks, kita ganti untungMaks dan tokoTerbaik

// dengan toko tersebut

for (toko = 1; toko < 25; toko++) {

// Hitung keuntungan toko tersebut sepanjang tahun

total = 0.0;

for (bulan = 0; bulan < 12; bulan++)

total += untung[toko][bulan];

// Bandingkan keuntungan toko ini dengan untungMaks

if (total > untungMaks) {

untungMaks = total; // keuntungan terbesar saat ini

tokoTerbaik = toko; // datang dari toko ini

}

} // akhir for

// Di sini, untungMaks adalah keuntungan terbesar dari 25 toko

// dan tokoTerbaik adalah toko dengan keuntung tersebut

// (Mungkin juga ada toko yang menghasilkan keuntungan

// yang persis sama.)

Pencarian, Pengurutan dan Rekursi

Dua jenis teknik pemrosesan array yang paling umum adalah pencarian dan pengurutan.

Pencarian di sini berarti mencari item di dalam array yang memenuhi kriteria tertentu.

Pengurutan berarti menata ulang semua item di dalam array dalam urutan tertentu (urutan

naik atau turun tergantung konteksnya).

Pencarian dan pengurutan sering didiskusikan, dengan cara yang teoretis, dengan

menggunakan contoh array bilangan. Dalam situasi sebenarnya, tipe data yang lebih

Page 216: Java With Eclipse

kompleks biasanya digunakan. Misalnya, array bisa jadi suatu mailing list, dan setiap

elemen di dalam array adalah objek yang berisi nama dalam alamat email. Kita mungkin

akan mencari alamat email seseorang apabila namanya diketahui. Ini adalah contoh

pencarian, di mana kita ingin mencari objek di dalam array yang berisi suatu nama.

Mungkin juga kita ingin mengurut array tersebut tergantung pada kriteria tertentu.

Misalnya mengurutkan elemen pada array di mana nama diurutkan secara abjad. Contoh

lainnya adalah mengurutkan elemen pada array berdasarkan kode pos.

Contoh-contoh ini bisa dibuat lebih umum yaitu kita mempunyai array yang berisi objek,

dan kita ingin melakukan pencarian atau pengurutan array berdasarkan nilai salah satu

variabel instansi pada array tersebut. Kita bisa gunakan beberapa istilah yang bersumber

pada "database", yang sesungguhnya adalah koleksi data yang besar dan terorganisir.

Kita akan menyebut masing-masing objek di dalam array sebagai record. Variabel instansi

di dalam objek disebut field dari record tersebut. Dalam contoh mailing list di atas,

masing-masing record berisi nama dan alamat email. Field dari record tersebut mungkin

nama depan, nama akhir, alamat email, dan seterusnya. Dalam konteks pencarian dan

pengurutan, salah satu field diambil sebagai field kunci. Mencarian berarti mencari suatu

record di dalam array yang memiliki nilai tertentu pada field kuncinya. Pengurutan berarti

melakukan penataan elemen di dalam array sehingga field kunci record akan terurut

berdasarkan urutan naik atau turun.

Dalam bagian ini, contoh-contoh yang disediakan akan mengikuti tradisi menggunakan

array bilangan. Kita akan contoh beberapa record dan kunci untuk mengingatkan kita pada

aplikasi yang lebih praktis.

Lebih jauh, kita akan melihat tentang rekursi dan aplikasinya. Suatu rubrutin disebut

rekursif jika ia memanggil dirinya sendiri baik secara langung atau tak langsung. Rekursi

dapat digunakan untuk menyelesaikan masalah kompleks dengan menguranginya menjadi

masalah yang lebih sederhana.

Pencarian

Ada algoritma sederhana yang bisa digunakan untuk mencari suatu item pada array : Lihat

setiap array, dan cek apakah isinya sama dengan item yang kita cari. Jika ketemu, maka

pencarian selesai. Jika kita sudah melihat semua item dan tidak ada item yang sama, maka

kita yakin bahwa item yang kita cari tidak ada dalam array.

Subrutin untuk mengimplementasikan algoritma tersebut mudah kita tulis. Misalnya array

yang kita cari bertipe int[]. Berikut ini adalah metode untuk mencari integer tertentu

dalam array. Jika integer ditemukan, maka metode ini akan mengembalikan indeks dalam

Page 217: Java With Eclipse

array di mana item tersebut ditemukan. Jika integer tersebut tidak ada dalam array, maka

metode tersebut akan mengembalikan nilai -1, yang artinya integer tersebut tidak

ditemukan.

static int cari(int[] A, int N) {

// Mencari integer N di dalam array A

// Kondisi akhir : jika N tidak ada dalam array

// maka kembalikan -1. Jika N ada dalam array

// kembalikan i, yaitu indeks di mana A[i] == N

for (int indeks = 0; indeks < A.length; indeks++) {

if ( A[indeks] == N )

return indeks; // N ditemukan pada indeks ini.

}

// Jika kita sampai di sini, berarti N belum ditemukan

// Kembalikan -1.

return -1;

}

Metode seperti ini dimana pencarian dilakukan dengan menguji setiap item disebut

pencarian linier (linear search). Jika kita tidak mengetahui apa-apa tentang isi dan urutan

item pada array, maka tidak ada lagi algoritma alternatif yang lebih baik dari ini. Akan

tetapi jika kita tahu bahwa elemen di dalam array diurut dalam urutan menaik atau urutan

menurun, maka kita bisa menggunakan algoritma lain yang lebih cepat. Tentu saja, waktu

yang dibutuhkan untuk mengurutkan array tidak sebentar, akan tetapi jika array ini akan

dicari berulang kali, maka waktu pengurutan array akan terbayarkan dengan cepat.

Pencarian biner (binary search) adalah metode untuk mencari suatu item dalam array yang

sudah diurutkan. Meskipun implementasinya tidak mudah, akan tetapi ide dasarnya sangat

mudah : Jika kita mencari suatu item dalam suatu array yang terurut, maka kita bisa

menghapus setengah dari keseluruhan elemen hanya dengan melihat satu nilai. Misalnya

kita ingin mencari bilangan 42 dalam array yang sudah diurutkan yang terdiri dari 1000

bilangan bulat. Anggap bahwa array tersebut diurutkan dalam urutan menaik (dari kecil ke

besar). Kemudian kita cek item ke-500 dalam array, dan ternyata isinya adalah 93. Karena

42 kurang dari 93, dan karena elemen di dalam array tersebut dalam urutan menaik, kita

bisa simpulkan bahwa 42 tidak mungkin ada di item ke-501 ke atas. Maka elemen tersebut

pasti ada di lokasi tertentu sebelum posisi ke-500.

Page 218: Java With Eclipse

Cara berikutnya adalah melihat di lokasi 250. Jika misanya lokasi tersebut berisi 21, maka

kita bisa menghilangkan lokasi 0 hingga 250 dan memfokuskan pencarian antara 251 dan

499. Yang berikutnya adalah kira-kira di lokasi ke-125 setelah itu, yang berikutnya adalah

sekitar 62 lokasi setelah itu. Setelah kira-kira 10 langkah pengujian, hanya ada satu lokasi

yang akan kita cek.

Cara ini akan jauh lebih mudah dan lebih cepat daripada harus mencari semua elemen di

dalam array. Jika ada satu juta elemen di dalam array, maka kita hanya perlu mencari 20

kali. (Secara matematika, jumlah langkah yang diperlukan adalah logaritmik dari jumlah

item di dalam array).

Untuk membuat subrutin pencarian biner pada Java yang mencari item N pada array A,

kita hanya perlu mencatat rentang lokasi di mana kira-kira N bisa ditemukan. Pada setiap

langkah, kita bisa mengurangi kemungkinan dan mengurangi rentang pencarian. Operasi

dasarnya adalah mencari item di tengah-tengah rentang tersebut. Jika item ini lebih besar

dari N, maka rentang bagian atasnya bisa dibuang. Jika kurang dari N, maka rentang

bawahnya bisa dibuang.

Jika nilai di tengah-tengah tersebut persisi sama denan N, maka pencarian selesai. Jika

ukurang pencarian berkurang menjadi nol, maka nilai N tidak ada dalam array. Berikut ini

adalah subrutin yang mengembalikan lokasi di mana N berada di dalam array terurut A.

Jika N tidak ditemukan, maka nilai -1 akan dikembalikan.

static int pencarianBiner(int[] A, int N) {

// Mencari bilangan N pada array A

// Kondisi awal : A harus diurut menaik (dari kecil ke besar)

// Kondisi akhir : Jika N ada dalam array, maka kembalikan

// nilai i, di mana A[i] == N. Jika tidak kembalikan -1

int lokasiTerkecil = 0;

int lokasiTerbesar = A.length - 1;

while (lokasiTerbesar >= lokasiTerkecil) {

int tengah = (lokasiTerkecil + lokasiTerbesar) / 2;

if (A[tengah] == N) {

// N ditemukan di sini

return tengah;

}

else if (A[tengah] > N) {

// buang lokasi >= tengah

lokasiTerbesar = tengah - 1;

Page 219: Java With Eclipse

}

else {

// buang lokasi <= tengah

lokasiTerkecil = tengah + 1;

}

}

// Sampai titik ini, lokasiTerbesar < lokasiTerkecil

// yang berarti nilai N tidak ada dalam array.

// Kembalikan -1, yang artinya item tidak ditemukan

return -1;

}

List Asosiasi

Salah satu aplikasi pencarian yang banyak digunakan adalah yang menggunakan list

asosiasi (association list). Contoh umum dari suatu list asosiasi adalah kamus. Kamus

menghubungan kata dengan definisi. Dengan kata tertentu, kita bisa menggunakan kamus

untuk mencari definisinya. Kita bisa membayangkan kamus sebagai daftar suatu pasangan

(pair) dalam bentuk (k,d) di mana k adalah kata dan d adalah definisi. Secara umum, kita

menganggap bahwa tidak ada dua pasangan dalam list yang memiliki kunci yang sama.

Operasi dasar dari list asosiasi adalah sebagai berikut : Dengan kunci k, cari nilai n yang

berhubungan dengan k, jika ada.

List asosiasi digunakan secara luas dalam ilmu komputer. Misalnya, suatu kompiler harus

melacak di mana lokasi suatu variabel pada memori. Kompiler dapat menggunakan list

asosiasi di mana kuncinya adalah nama variabel dan nilainya adalah alamat variabel

tersebut di memori. Contoh lainnya adalah mailing list, yang menghubungkan nama dan

alamat email dalam daftar tersebut. Contoh lain yang mungkin berhubungan adalah

direktori telepon yang menghubungkan nama dan nomor telepon. Item di dalam list

tersebut mungkin berupa objek dari kelas :

class EntriTelepon {

String nama;

String noTelp;

}

Data dalam direktori telepon adalah array yang bertipe EntriTelepon[] dan variabel

integer untuk menyimpan berapa banyak item yang disimpan dalam direktori tersebut.

(Contoh ini adalah contoh dari "array setengah penuh" yang dibahas pada bagian

Page 220: Java With Eclipse

sebelumnya. Mungkin lebih baik jika kita menggunakan array dinamis atau ArrayList

untuk menyimpan daftar telepon.) Direktori telepon mungkin berupa objek di dalam kelas

class DirektoriTelepon {

EntriTelepon[] info = new EntriTelepon[100]; // Tempat untuk 100

entri

int jmlEntri = 0; // Jumlah entri aktual di dalam array

void tambahEntri(String nama, String noTelp) {

// Tambah entri baru di akhir array

info[jmlEntri] = new EntriTelepon();

info[jmlEntri].nama = nama;

info[jmlEntri].noTelp = noTelp;

jmlEntri++;

}

String getNoTelp(String nama) {

// Ambil nomor telepon dari nama ini atau

// kembalikan null jika tidak ada nama ini

// di dalam array.

for (int idks = 0; idks < jmlEntri; idks++) {

if (nama.equals( info[idks].nama )) // Nama ketemu!

return info[idks].noTelp;

}

return null; // Nama tidak ketemu.

}

}

Lihat bahwa metode getNoTelphanya mengambil lokasi dalam array yang telah diisi

dengan EntriTelepon. Dan juga tidak seperti rutin pencarian yang disebutkan

sebelumnya, rutin ini tidak mengembalikan lokasi item dalam array. Akan tetapi ia

mengembalikan nilai lain yang berhubungan dengan kata kuncinya, yaitu nama. Hal ini

sering dilakukan untuk list asosiasi.

Kelas ini bisa diperbaliki lebih lanjut. Satu hal, mungkin lebih baik jika kita melakukan

pencarian dengan menggunakan pencarian biner dan bukan pencarian linier sederhana

dalam metode getNoTelp. Akan tetapi, kita hanya bisa lakukan ini apabila EntriTelepon

diurut terlebih dahulu berdasarkan nama. Dan sebenarnya, tidak terlalu suit untuk membuat

entri tersebut dalam urutan, yang akan kita lihat berikutnya.

Pengurutan

Page 221: Java With Eclipse

Pengurutan Penyisipan (Insertion Sort)

Ada banyak algoritma yang tersedia untuk melakukan pengurutan. Salah satu yang paling

mudah dimengerti adalah pengurutan penyisipan (insertion sort). Metode ini juga bisa

digunakan untuk menjaga agar list selalu dalam urutan tertentu (naik atau turun) sewaktu

kita menambah item baru ke dalam list. Mari kita lihat contoh pertama :

Misalnya kita memiliki list yang sudah diurutkan, dan kita ingin menambahkan sebuat item

ke dalamnya. Jika kita ingin memastikan bahwa suatu list tetap dalam kondisi terurut,

maka item tersebut harus diletakkan di tempat yang tepat, yaitu semua item yang lebih

kecil harus ditempatkan sebelum item tersebut, dan semua item yang lebih besar

ditempatkan setelahnya. Artinya kita harus menggeser semua item yang lebih besar ke satu

sisi untuk memberi ruangan pada item baru yang akan ditambahkan.

static void sisip(int[] A, int jmlItem, int itemBaru) {

// Kondisi awal : jmlItem adalah jumlah item pada A.

// Item ini harus berada dalam kondisi terurut naik di mana

// (A[0] <= A[1] <= ... <= A[jmlItem-1]).

// Ukuran array harus lebih besar dari jmlItem.

// Kondisi akhir : jumlah item akan ditambah dengan satu,

// itemBaru telah ditambah pada array, dan semua item

// masih dalam kondisi terurut.

// Catatan: untuk menyelesaikan proses penyisipan item

// dalam array, variabel yang mencatat jumlah item dalam

// array harus ditambah satu setelah memanggil subrutin ini.

int lok = jmlItem - 1; // Mulai dari akhir array

/* Pindahkan item yang lebih besar dari itemBaru satu posisi;

Stop jika item yang lebih kecil ditemukan atau sampai ke

awal array (lok == 0) */

while (lok >= 0 && A[lok] > itemBaru) {

A[lok + 1] = A[lok]; // Pindahkan lokasi item dari posisi lok ke

lok+1

lok = lok - 1; // Pindah ke lokasi sebelumnya

}

A[lok + 1] = itemBaru; // Letakkan itemBaru di tempat kosong

}

Page 222: Java With Eclipse

Metode di atas bisa dikembangkan menjadi metode pengurutan jika kita mengeluarkan

semua item dari array yang belum diurutkan, kemudian memasukkannya kembali satu

demi satu, sambil menjaga agar array tetap terurut selama kita menambah item ke array

baru. Setiap penyisipan bisa dilakukan dengan rutin sisip() di atas. Dalam algoritma

sesungguhnya, kita tidak benar-benar mengambil semua item dari dalam array, kita hanya

cukup mengingat bagian mana yang sudah diurutkan.

static void urutPenyisipan(int[] A) {

// Mengurutkan array A dalam urutan menaik (dari kecil ke besar)

int itemTerurut; // Jumlah item yang sudah diurut

for (itemTerurut = 1; itemTerurut < A.length; itemTerurut++) {

// Anggap item A[0], A[1], ... A[itemTerurut-1]

// telah diurutkan. Sisipkan A[itemTerurut]

// ke dalam bagian yang sudah diurutkan

int temp = A[itemTerurut]; // Item yang akan disisipkan

int lok = itemTerurut - 1; // Mulai dari akhir list

while (lok >= 0 && A[lok] > temp) {

A[lok + 1] = A[lok]; // Pindahkan item dari lok ke lok+1

lok = lok - 1; // Pindah ke lokasi sebelumnya

}

A[lok + 1] = temp; // Letakkan temp di tempat kosong

}

}

Ilustrasi berikut adalah ilustrasi di tengah-tengah pengurutan, yang menunjukkan apa yang

terjadi di tengah-tengah salah satu eksekusi perulangan for dari kode di atas, ketika

itemTerurut = 5.

Page 223: Java With Eclipse

Pengurutan Pilihan (Selection Sort)

Metode pengurutan lainnya yang biasa digunakan adalah pengurutan pilihan (selection

sort). Metode ini mencari item terbesar di dalam list, kemudian memindahkannya ke akhir

array -- atau dengan kata lain di tempatnya, karena item terbesar akan berada di akhir

array. Setelah item terbesar ditempatkan di tempat yang benar, kita gunakan cara yang

sama, yaitu cari item terbesar berikutnya, kemudian letakkan di tempat kedua dari akhir,

dan seterusnya. Metode ini dapat ditulis sebagai :

static void urutPilihan(int[] A) {

// Urut A dengan urutan menaik dengan Pengurutan Pilihan

for (int tmptTerakhir = A.length-1; tmptTerakhir > 0; tmptTerakhir--)

{

// Cari nilai terbesar di antara A[0], A[1], ...,

A[tmptTerakhir],

// dan pindahkan ke tmptTerakhir dengan cara menukarny

// dengan nilai yang ada di tmptTerakhir

Page 224: Java With Eclipse

int lokMaks = 0; // Lokasi nilai terbesar saat ini

for (int j = 1; j <= tmptTerakhir; j++) {

if (A[j] > A[lokMaks]) {

// Karena A[j] lebih besar dari nilai maksimum yang

pernah

// kita lihat, j adalah lokasi baru tempat di mana nilai

// maksimum tersebut berada

lokMaks = j;

}

}

int temp = A[lokMaks]; // Tukar nilainya dengan A[tmptTerakhir].

A[lokMaks] = A[tmptTerakhir];

A[tmptTerakhir] = temp;

} // akhir perulangan

}

Pengurutan penyisipan dan pengurutan pilihan cocok digunakan untuk mengurut array

dengan ukuran kecil (hingga beberapa ratus elemen). Ada beberapa algoritma pengurutan

lain yang jauh lebih cepat dari pengurutan penyisipan dan pengurutan pilihan untuk array

yang sangat besar. Kita akan diskusikan kemudian.

Mengacak Nilai

Kita akan sudahi bagian ini dengan masalah yang sedikit berkaitan, lebih jarang muncul,

akan tetapi menarik, yaitu bagaimana caranya meletakkan elemen array dengan urutan

acak. Misalnya adalah untuk mengocok kartu. Algoritma baik untuk mengocok mirip

dengan pengurutan pilihan, akan tetapi kita tidak memindahkan item terbesar ke array

paling belakang. Item dipilih secara acak dan dipindahkan ke akhir array. Berikut ini

adalah subrutin untuk mengocok array bertipe int.

static void kocok(int[] A) {

// Kondisi akhir : Item pada A diatur dalam urutan yang acak

for (int tmptTerakhir = A.length-1; tmptTerakhir > 0; tmptTerakhir--)

{

// Pilih lokasi acak di antara 0,1,...,tmptTerakhir.

int lokAcak = (int)(Math.random()*(tmptTerakhir+1));

// Tukar item pada lokAcak dengan A[tmptTerakhir]

int temp = A[lokAcak];

A[lokAcak] = A[tmptTerakhir];

Page 225: Java With Eclipse

A[tmptTerakhir] = temp;

}

}

Rekursi

Definisi rekursi adalah definisi yang menggunakan konsep atau sebagian dari definisi

tersebut menjadi definisi yang komplit.

Misalnya : "keturunan" bisa berarti anak atau keturunan dari anak. "Kalimat" bisa berarti

dua kalimat yang digabung dengan kata hubung "dan". "Direktori" adalah bagian pada hard

disk yang berisi file dan direktori. Dalam matematika, "himpunan" adalah koleksi elemen,

di mana elemen tersebut bisa berupa himpunan. "Pernyataan" pada Java misalnya

pernyataan while yang didalamnya terdapat kata while, kondisi bernilai boolean dan

pernyataan lainnya.

Definisi rekursi bisa menjelaskan situasi yang sangat kompleks dalam beberapa kata.

Definisi istilah "keturunan" tanpa menggunakan rekursi bisa jadi "anak, cucu, cicit, dan

seterusnya". Akan tetapi mengatakan "dan seterusnya" bukan arti "keturunan" secara

lengkap.

Kita juga akan kesulitan jika kita mencoba mendefinisikan "direktori" sebagai "file yang

berisi daftar file, dimana beberapa file bisa berisi daftar file, di mana beberapa file tersebut

bisa berisi daftar file, dan seterusnya". Mencoba untuk menjelaskan pernyataan Java tanpa

menggunakan rekursi dalam definisinya akan sulit dilakukan.

Rekursi bisa digunakan dalam teknik pemrograman. Subrutin rekursif adalah subrutin yang

memanggil dirinya sendiri, baik langsung maupun tak langsung. Subrutin tersebut

memanggil dirinya sendiri secara tidak langsung yaitu jika ia memanggil subrutin lain yang

akhirnya memanggil subrutin pertama (baik langsung maupun tak langsung).

Suatu subrutin rekursi bisa menyelesaikan tugas kompleks dalam beberapa baris perintah.

Kita akan lihat beberapa contohnya pada bagian ini.

Mari kita mulai dengan contoh yang sudah kita lihat sebelumnya: algorithma pencarian

biner pada bagian sebelumnya. Pencarian biner digunakan untuk mencari suatu nilai dalam

list terurut (atau jika item nilai tersebut tidak ada di dalam list tersebut, akan memberitahu

hasilnya misalnya dengan mengembalikan -1).

Caranya adalah dengan mengecek elemen di tengah list tersebut. Jika elemen tersebut sama

dengan nilai yang dicari, maka pencarian tersebut selesai. Jika nilai yang dicari lebih kecil

daripada nilai elemen di tengah list tersebut, maka kita harus mencari di separuh awal dari

Page 226: Java With Eclipse

list tersebut. Jika lebih besar, kita harus mencari di separuh akhir list tersebut. Kemudian,

pada separuh list yang kita pilih tersebut, kita akan mengecek kembali elemen tengahnya.

Kita akan melihat kembali apakah nilainya sama dengan nilai yang kita cari, lebih besar

atau lebih kecil, yang dari sini kita tahu paruh mana yang akan kita cari berikutnya. Dan

begitu seterusnya, hingga besar list yang akan dicari berkurang menjadi 0.

Ini adalah definisi rekursif, dan kita bisa membuat subrutin rekursif untuk

mengimplementasikannya.

Sebelumnya, ada dua pertimbahangan yang harus kita masukkan ke dalam perhitungan

kita, yang merupakan fakta tentang subrutin rekursif. Pertama, algoritma pencarian biner

dimulai dengan mengecek "elemen tengah suatu list". Apa yang terjadi jika list tersebut

kosong? Jika tidak ada elemen di dalam list, maka kita tidak mungkin melihat elemen di

tengahnya. Atau dengan kata lain, ini disebut "kondisi awal" untuk mengecek elemen di

tengah list, yaitu memiliki list yang tidak kosong.

Apa yang terjadi kita ternyata harus mencari nilai di list kosong? Jawabannya mudah : Kita

bisa mengatakan bahwa nilai yang kita cari tidak ada di dalam list. List kosong adalah

kasus dasar untuk algoritma pencari biner. Kasus dasar untuk algoritma rekursif adalah

kasus yang akan ditangani secara langsung, bukan dilakukan secara rekursif. Algoritma

pencarian biner memiliki kasus dasar lain, yaitu jika kita menemukan nilai yang kita cari di

tengah suatu list, maka program tersebut selesai. Kita tidak perlu melakukan rekursi lebih

lanjut.

Pertimbangan kedua adalah parameter subrutin tersebut. Dalam subrutin non-rekursif dari

pencarian biner yang dibahas sebelumnya, parameternya adalah suatu array. Akan tetapi

dalam pendekatan rekursif, kita harus bisa menerapkan subrutin secara rekursif hanya

sebagian dari list aslinya. Pada subrutin aslinya yang non-rekursif, kita melakukan

pencarian di seluruh array, sedangkan subrutin rekursif harus bisa mencari di sebagian

array. Parameter subrutin tersebut harus bisa memberi tahu di bagian mana array akan

dicari.

Berikut ini adalah algoritma pencarian biner rekursif yang mencari suatu nilai dalam

bagian dari array integer:

static int cariBiner(int[] A, int idksRendah, int idksTinggi, int nilai)

{

// Cari "nilai" pada array "A" dari posisi "idksRendah" ke

"idksTinggi",

// Asumsinya adalah array diurut dalam urutan menaik

// Jika nilai ditemukan kembalikan indeks pada array

Page 227: Java With Eclipse

// dimana nilai tersebut berada. Jika tidak kembalikan -1

if (idksRendah > idksTinggi) {

// Artinya list kosong karena seharusnya idksRendah lebih rendah

// dari idksTinggi. "Nilai" tidak mungkin ada di list kosong.

return -1;

}

else {

// Cek elemen di tengah list. Jika nilai sama dengan isi elemen

// tengah, maka kembalikan posisi tersebut.

// Jika tidak, cari secara rekursif di awal atau akhir

// dari setengah list

int idksTengah = (idksRendah + idksTinggi) / 2;

if (nilai == A[idksTengah])

return idksTengah;

else if (nilai < A[idksTengah])

return cariBiner(A, idksRendah, idksTengah - 1, nilai);

else // nilai > A[idksTengah]

return cariBiner(A, idksTengah + 1, idksTinggi, nilai);

}

} // akhir cariBiner()

Dalam rutin di atas, parameter idksRendah dan idksTinggi menyatakan bagian array

yang akan dicari. Untuk mencari keseluruhan array, kita hanya perlu memanggil

cariBiner(A, 0, A.length - 1, nilai). Dalam kedua kasus dasar -- yaitu tidak ada

elemen di dalam rentang indeks yang diberikan dan ketika tidak ada nilai yang ditemukan

di tengah-tengah rentang tersebut -- subrutin dapat memberikan jawabannya secara

langsung, tanpa rekursi. Pada kasus lain, subrutin akan memanggil dirinya sendiri secara

rekursif untuk menghitung dan mengembalikan hasilnya.

Banyak orang yang merasa sulit untuk memahami bagaimana rekursi bekerja. Kuncinya

ada 2 hal yang harus dipenuhi agar rekursi bisa bekerja dengan benar :

Harus ada minimum satu kasus dasar, yang bisa ditangani tanpa menggunakan

rekursi.

Jika subrutin dilakukan secara rekursif, ia harus dipanggil dalam lingkup yang lebih

kecil, makin kecil hingga mendekati kasus dasarnya.

Satu kesalahan umum yang terjadi ketika menulis subrutin rekursif adalah jika kita

melanggar salah satu dari kedua aturan di atas. Jika aturan ini dilanggar, hasilnya bisa jadi

Page 228: Java With Eclipse

rekursi tak hingga, di mana subrutin tersebut akan memanggil dirinya terus menerus tanpa

henti, karena tidak pernah mencapai kasus dasarnya.

Rekrusi tak hingga mirip dengan perulangan yang tak pernah berhenti. Akan tetapi karena

setiap panggilan rekursif menggunakan memori komputer, maka program yang tersangkut

di dalam rekursi tak hingga, pada akhirnya akan kehabisan memori.

Pencarian biner dapat diimplementasikan dengan perulangan while selain dengan

menggunakan rekursi. Sekarang mari kita lihat suatu masalah yang lebih mudah

diselesaikan dengan rekursi tapi sulit dilakukan dengan metode lain. Berikut ini adalah

contoh yang biasa digunakan, yand dinamakan "Menara Hanoi".

Persoalannya dapat dituliskan sebagai berikut : sebuah tumpukan piringan dengan ukuran

berbeda ditumpuk dari urutan terbesar di bagian paling bawah hingga terkecil di bagian

paling atas. Seluruh piringan harus dipindahkan dari satu tiang ke tiang lain, dengan dua

aturan

dalam setiap langkah hanya boleh memindahkan satu piringan, dan

piringan yang lebih besar tidak boleh diletakkan di atas piringan yang lebih kecil.

Ada tiang lain yang berfungsi sebagai tiang cadangan, dan bisa digunakan sebagai tempat

sementara. Gambar berikut (atas) mengilustrasikan tumpukan 10 piringan. Gambar lainnya

(bawah) adalah ilustrasi setelah beberapa langkah telah dijalankan.

Kita akan memindahkan 10 piringan dari tiang 0 ke tiang 1, dan tiang 2 bisa dijadikan

cadangan. Bisakah kita mengurangi persoalan ini menjadi persoalan yang sama dengan

Page 229: Java With Eclipse

skala yang lebih kecil? Mungkin kita harus sedikit mengeneralisir persoalannya sehingga

kita bisa melihat lebih jelas.

Jika ada N piringan pada tiang 0, kita tahu bahwa pada akhirnya kita haris memindahkan

piringan paling bawah dari tiang 0 ke tiang 1. Akan tetapi sebelum itu, menurut aturan di

atas, piringan 0 hingga N-1 harus dipindahkan terlebih dahulu ke tiang 2. Setelah kita

pindahkan piringan ke-N ke tiang 1, maka kita harus memindahkan kembali N-1 piringan

dari tiang 2 ke tiang 1 untuk menyelesaikan masalahnya. Lihat bahwa memindahkan N-1

piringan adalah persoalan yang sama seperti memindahkan N piringan, akan tetapi

sekarang persoalannya menjadi lebih kecil.

Ini bisa dilakukan dengan mudah dengan rekursi.

Suatu persoalan harus digeneralisir terlebih dahulu, di sini kita lihat bahwa persoalan yang

lebih kecil adalah memindahkan piringan dari tiang 0 ke tiang 2 dan kemudian dari tiang 2

ke tiang 1, bukan dari tiang 0 ke tiang 1. Untuk subrutin rekursiif yang akan kita buat, kita

harus menyatakan tiang asal, tiang tujuan, dan tiang cadangannya. Kasus dasarnya adalah

jika hanya ada satu piringan yang akan dipindahkan, yang mana jawabannya mudah :

pindahkan satu piringan itu dalam satu langkah.

Berikut ini adalah contoh subrutin untuk menyelesaikan masalah ini :

void MenaraHanoi(int piringan, int asal, int tujuan, int cadangan) {

// Memecahkan persoalan untuk memindahkan sejumlah "piringan"

// dari tiang "asal" ke tiang "tujuan". Tiang "cadangan" bisa

// digunakan sebagai tempat sementara

if (piringan == 1) {

// Hanya ada satu piringan, pindahkan langsung

System.out.println("Pindahkan piringan dari tiang "

+ asal + " ke tiang " + tujuan);

}

else {

// Pindahkan semua piringan minus 1 ke tiang cadangan,

// kemudian pindahkan piringan paling bawah ke tiang tujuan,

// kemudian pindahkan sisanya dari tiang cadangan ke

// tiang tujuan.

MenaraHanoi(piringan-1, asal, cadangan, tujuan);

System.out.println("Pindahkan piringan dari tiang "

+ asal + " ke tiang " + tujuan);

MenaraHanoi(piringan-1, cadangan, tujuan, asal);

}

}

Page 230: Java With Eclipse

Subrutin ini mengekspresikan solusi persoalan secara alami. Rekursi bisa bekerja dengan

benar karena setiap panggil rekursif selalu akan dipanggil dengan jumlah piringan yang

semakin sedikit. Dan ketika sampai pada kasus dasarnya, yaitu hanya ada satu piringan,

persoalannya bisa diselesaikan langsung dengan memindahkan satu piringan tersebut ke

tiang tujuannya.

Untuk memecahkan persoalan ini pada "level paling atas", yaitu memindahkan N piringan

dari tiang 0 ke tiang 1, subrutin ini bisa dipanggil dengan perintah

MenaraHanoi(N,0,1,2).

Ceritanya, dahulu kala ada kisah yang merupakan nama dari persoalan ini. Menurut cerita

ini, pada saat bumi pertama kali dicipkatan, sekumpulan biksu pada suatu menara di dekat

Hanoi diberi tumpukan 64 piringan dan diberi tugas untuk memindahkan satu piringan

setiap hari dengan aturan yang sama seperti di atas. Pada hari ketika tugas ini selesai, alam

semesta akan kiamat. Tapi tenang saja, karena jumlah langkah yang diperlukan untuk

menyelesaikan tugas ini untuk N piringan adalah 2N - 1, dan 264 - 1 hari sama dengan lebih

dari 50 trilyun tahun.

Pengurutan Cepat

Aplikasi rekursi yang cukup populer adalah Pengurutan Cepat (Quicksort) yang digunakan

untuk mengurutkan array. Di bagian sebelumnya kita telah melihat bagaimana

mengurutkan array dengan menggunakan pengurutan pilihan dan pengurutan penyisipan

yang relatif lebih mudah, akan tetapi akan berjalan lebih lambat untuk array yang besar.

Algoritma pengurutan yang lebih cepat sudah tersedia. Salah satunya adalah pengurutan

cepat (quick sort), yaitu algoritma rekursif yang ternyata paling cepat hampir pada segala

kondisi.

Algoritma pengurutan cepat dibuat berdasarkan ide yang sederhana namun cerdas : Dari

beberapa item, pilih satu item. Item ini kita sebut pivot. Pindahkan semua item yang lebih

kecil dari pivot ke awal array, dan pindahkan item yang lebih besar ke akhir array.

Kemudian letakkan pivot di tengah-tengah kedua grup tersebut. Atau dengan kata lain,

posisi pivot adalah posisi terakhir dan tidak perlu dipindahkan lagi.

Page 231: Java With Eclipse

LangkahUrutCepat bukan algoritma rekursif. Kecepatan pengurutan cepat tergantung dari

kecepatan LangkahUrutCepat. Karena ini bukan diskusi utama kita, kita akan tuliskan

algoritma LangkahUrutCepat tanpa diskusi lebih lanjut.

static int LangkahUrutCepat(int[] A, int rendah, int tinggi) {

// Jalankan LangkahUrutCepat pada array dengan indeks

// antara rendah dan tinggi pada array A. Nilai yang dikembalikan

// adalah posisi akhir pivot dalam array

// Kita ambil sembarang pivot, yaitu pada indeks pertama

int pivot = A[rendah];

// Nilai antara rendah dan tinggi adalah nilai yang belum pernah

// kita uji. Kurangi tinggi dan naikkan rendah hingga keduanya

// bernilai sama, pindahkan nilai yang lebih besar dari pivot

// sehingga nilai tersebut berada di atas tinggi dan pindahkan

// nilai yang lebih kecil dari pivot sehingga nilainya berada

// di bawah rendah. Ketika kita mulai, A[rendah] adalah tempat

// kosong, karena ini adalah posisi awal nilai pivot

while (tinggi > rendah) {

while (tinggi > rendah && A[tinggi] > pivot) {

// Pindahkan tinggi hingga melewati nilai yang lebih rendah

// dari pivot. Nilai ini tidak perlu dipindahkan

hi--;

}

if (tinggi == rendah)

break;

Page 232: Java With Eclipse

// Nilai pada A[tinggi] kurang dari pivot. Pindahkan ke tempat

// kosong pada A[rendah], sehingga tempat di A[tinggi]

// menjadi kosong

A[rendah] = A[tinggi];

rendah++;

while (tinggi > rendah && A[rendah] < pivot) {

// Pindahkan rendah hingga melewati nilai yang lebih

tinggai

// pivot. Nilai ini tidak perlu dipindahkan

rendah++;

}

if (tinggi == rendah)

break;

// Nilai A[rendah] lebih tinggi dari pivot. Pindahkan ke tempat

// kosong pada A[tinggi], sehingga tempat di A[rendah]

// menjadi kosong.

A[tinggi] = A[rendah];

tinggi--;

} // akhir while

// Di sini, rendah sama dengan tinggi, dan ada tempat kosong

// di sini. Posisi ini berada di antara nilai yang lebih tinggi dan

// nilai yang lebih rendah dari pivot. Letakkan pivot di sini

// dan kembalikan posisinya.

A[rendah] = pivot;

return rendah;

} // akhir LangkahUrutCepat

Degan subrutin ini, maka pengurutan cepat mudah dilakukan. Algoritma untuk

mengurutkan deretan nilai yaitu dengan menjalankan LangkahUrutCepat pada nilai-nilai

tersebut, kemudian menjalankan pengurutan cepat secara rekursif terhadap item yang ada

di sebelah kiri pivot dan item yang ada di sebelah kanan pivot. Tentunya kita juga

Page 233: Java With Eclipse

membutuhkan kasus dasar. Yaitu jika list hanya memiliki satu item atau kosong, maka list

tersebut telah diurutkan.

static void urutcepat(int[] A, int rendah, int tinggi) {

// Jalankan pengurutan cepat untuk mengurutkan array

// antara posisi rendah dan posisi tinggi dalam urutan naik

if (tinggi <= rendah) {

// List memiliki panjang nol atau 1. Tidak ada yang perlu

// dilakukan, jadi kita keluar dari subrutin

return;

}

else {

// Jalankan LangkahUrutCepat dan dapatkan posisi pivot

// Kemudian jalankan urutcepat untuk mengurut item sebelum

// pivot dan item setelah pivot

int posisiPivot = LangkahUrutCepat(A, rendah, tinggi);

urutcepat(A, rendah, pivotPosition - 1);

urutcepat(A, pivotPosition + 1, tinggi);

}

}

Seperti biasa, kita telah melihat masalah dengan mengeneralisirnya. Masalah awalnya

adalah untuk mengurutkan array, akan tetapi algoritma rekursif dibuat untuk mengurutkan

sebagian array. Untuk mengurut keseluruhan array A, kita bisa menggunakan

urutcepat() yaitu dengan perintah urutcepat(A, 0, A.length -1).

Struktur Data Berantai

Pada bagian ini kita akan melihat teknik pemrograman tingkat lanjut lainnya, yaitu struktur

data berantai (linked data structure), dan applikasinya.

Suatu referensi ke suatu objek bisa disimpan sebagai variabel instansi di objek lain,

sehingga "terkait" satu sama lain. Struktur data kompleks bisa dibuat dengan mengaitkan

satu objek dengan objek lainnya menjadi struktur data berantai.

Yang menarik adalah apabila objek ini dikaitkan dengan objek lain yang memiliki kelas

yang sama. Di sini kelas tersebut digunakan dalam definisi kelas yang sama. Beberapa

jenis struktur data bisa dibuat dengan menggunakan model semacam ini.

Mengaitkan Objek

Hampir semua objek memiliki variabel instansi. Jika variabel instansi bertipe suatu kelas

atau interface, maka variabel instansi itu bisa menyimpan referensi ke objek lain. Referensi

disebut juga pointer, karena referensi menunjuk pada suatu objek. (Variabel yang berisi

Page 234: Java With Eclipse

referensi ke suatu objek juga bisa berisi null alias tidak menunjuk ke mana-mana). Jika

satu objek memiliki referensi ke objek lain, objek itu disebut terkait satu sama lain.

Struktur data kompleks bisa dibuat dengan mengaitkan objek satu sama lain.

Jika suatu objek memiliki pointer ke objek lain dengan tipe yang sama, maka definisi

kelasnya bersifat rekursif. Rekursi jenis ini banyak terjadi, misalnya kelas Pegawai yang

melambangkan pegawai di suatu perusahaan. Semua orang kecuali bos tertinggi memiliki

atasan, di mana atasannya ini adalah juga pegawai perusahaan tersebut. Tentunya kelas

Pegawai memiliki variabel instansi dengan tipe Pegawai yang menunjuk pada atasan

pegawai itu, sehingga :

class Pegawai {

// Objek untuk menyimpan data tentang seorang pegawai

String nama; // Nama pegawai

Pegawai atasan; // Atasan pegawai ini

.

. // (Metode dan variabel instansi lain.)

.

} // akhir kelas Pegawai

Jika pgw adalah variabel bertipe Pegawai, maka pgw.atasan adalah variabel lain yang

juga bertipe Pegawai. Jika pgw menunjuk pada bos tertinggi, maka pgw.atasan berisi

null yang artinya ia tidak memiliki atasan. Kita bisa mencetak nama atasan seorang

pegawai, misalnya dengan pernyataan Java berikut :

if ( pgw.atasan == null) {

System.out.println( pgw.nama " adalah bos tertinggi." );

}

else {

System.out.print( "Atasan dari " + pgw.nama + " ialah " );

System.out.println( pgw.atasan.nama );

}

Sekarang, misalnya kita ingin tahu berapa tingkat di atas seorang pegawai hingga sampai

pada bos tertinggi. Jika bisa melacak atasan dari atasannya, dan seterusnya, hingga sampai

pada bos tertinggi, kemudian menghitung berapa langkah yang diperlukan hingga sampai

ke bos tertinggi itu:

if ( pgw.atasan == null ) {

System.out.println( pgw.nama " adalah bos tertinggi." );

}

else {

Page 235: Java With Eclipse

Pegawai pointer; // Untuk menunjuk pada urutan bos.

pointer = pgw.supervisor;

if ( pointer.atasan == null) {

System.out.println( pgw.nama

+ " memiliki atasan langsung bos tertinggi." );

}

else {

int level = 0;

while ( pointer.atasan != null ) {

level++; // Level atasan

pointer = pointer.atasan;

}

System.out.println( "Ada " + level

+ " atasan antara " + pgw.nama

+ " dan bos tertinggi." );

}

}

Ketika perulangan while dieksekusi, pointer akan menunjuk pada atasan pgw, kemudian

atasannya lagi, dan seterusnya. Variabel level dinaikkan 1 setiap kali pointer menunjuk

pada atasan baru. Perulangan selesai ketika pointer.atasan berisi null yang artinya

pointer telah sampai pada bos tertinggi. Pada saat itu, level telah mencatat berapa level

yang dibutuhkan dari pgw ke bos tertinggi.

Dalam contoh ini, variabel atasan terlihat natural dan berfungsi dengan baik. Sebenarnya,

struktur data yang dibangun dengan mengaitkan objek satu sama lain sangat berguna,

sehingga sering digunakan sebagai topik pembahasan dalam ilmu komputer. Kita akan

melihat beberapa contohnya. Pada bagian ini dan berikutnya, kita akan melihat yang

dinamakan list berantai (linked list).

Page 236: Java With Eclipse

List berantai merupakan rantai objek yang bertipe sama, yang terkait oleh pointer dari satu

objek ke objek lain. Mirip dengan hirarki organisasi antara pgw hingga bos tertinggi pada

contoh di atas. Kita bisa juga memiliki siatuasi yang lebih kompleks di mana satu objek

berisi pointer ke beberapa objek. Kita akan lihat contohnya di bagian berikutnya.

Di bagian ini, list berantai akan dibuat dari objek yang bertipe Simpul yang didefinisikan

sebagai berikut :

class Simpul {

String item;

Simpul berikut;

}

Istilah simpul sering digunakan untuk menyebut salah satu objek di dalam struktur data

berantai. Objek dengan tipe Simpul bisa disambung seperti pada gambar di atas. Simpul

terakhir dalam list tersebut bisa ditentukan apabila variabel instansi berikut berisi null.

Meskipun Simpul pada contoh ini terlihat sederhana, kita bisa menggunakannya untuk

mengilustrasikan operasi umum pada list berantai. Operasi umumnya meliputi menghapus

simpul dari list, menyisipkan simpul baru ke dalam list, dan mencari String pada item di

dalam list. Kita akan lihat beberapa subrutin yang melakukan operasi ini.

Agar list berantai bisa digunakan dalam program, program tersebut perlu variabel untuk

menunjuk pada simpul pertama dari list. Variabel itu hanya perlu menunjuk pada simpul

Page 237: Java With Eclipse

pertama, karena simpul lainnya bisa dicari dari simpul tertama kemudian mengurutkan

melalui pointer ke simpul berikutnya.

Kita bisa membuat objek baru dengan misalnya bernama "ListString" yang memiliki

variabel instansi bernama "kepala". Variabel ini bertipe Simpul dan menyimpan referensi

ke simpul pertama dari list berantai. Jika listnya kosong, maka kepala berisi null.

public class ListString {

Simpul kepala;

.

.

. // metode dan variabel instansi lainnya

}

Misalnya kita ingin tahu apakah suatu string, itemDicari ada di salah satu simpul di

dalam list. Kita bisa membandingkan itemDicari dengan isi setiap simpul di dalam list.

Caranya, kita akan menggunakan variabel bertipe Simpul yang bernama pointer untuk

digunakan sebagai penunjuk ke simpul-simpul yang akan kita lihat. Kita hanya bisa

mengakses list melalui variabel kepala, jadi kita bisa mulai dengan mengisi pointer

dengan isi kepala.

Simpul pointer = kepala; // Mulai dari simpul pertama.

Kita harus membuat variabel baru ini karena kita akan mengganti isinya pada saat

melakukan pencarian. Kita tidak bisa mengganti isi kepala, karena jika kita ganti, kita

akan kehilangan jejak list yang kita buat. Untuk pindah dari satu simpul ke simpul

berikutnya, kita cukup menggunakan perintah pointer = pointer.berikut;. Kita akan

tahu bahwa kita telah sampai pada akhir list jika pointer bernilai null.

Semuanya bisa kita tuangkan dalam metode instansi cari() pada kelas ListString sebagai

berikut :

public boolean cari(String itemDicari) {

// Kembalikan true jika itemDicari ada di dalam list

// false jika tidak ada dalam list.

Page 238: Java With Eclipse

Simpul pointer; // Pointer untuk menelusuri list

pointer = kepala;

// Mulai pencarian dari kepala list (kepala adalah variabel instansi)

while ( pointer != null ) {

// Lihat isi simpul satu per satu. Jika isinya sama dengan

// yang kita cari, kembalikan true, jika tidak

// teruskan pencarian di simpul berikutnya

if ( pointer.item.equals(itemDicari) )

return true;

pointer = pointer.berikut; // Pindah ke simpul berikutnya

}

// Di sini, kita telah sampai pada akhir list

// akan tetapi tidak ada item yang kita cari.

// Kembalikan false, yang artinya kita tidak menemukan item ini.

return false;

} // akhir cari()

Pola di atas akan sering digunakan nanti: Jika kepala adalah variabel yang menunjuk pada

suatu list berantai, maka untuk proses semua simpul dalam list, kita bisa lakukan dengan :

pointer = kepala;

while ( pointer != null ) {

.

. // proses simpul yang ditunjuk oleh pointer

.

pointer = pointer.berikut;

}

Mungkin saja listnya kosong, yaitu apabila isi kepala adalah null. Dalam contoh kode di

atas, jika kepala berisi null, maka badan perulangan while tidak akan dieksekusi sama

sekali, karena kondisi perulangan hanya bisa dijalankan apabila pointer bernilai null.

Menyisipkan item baru ke dalam list sedikit lebih sulit. Dalam kelas ListString, item

pada simpul dibuat dalam urutan menaik. Jika suatu item ditambahkan ke dalam list, item

tersebut harus ditempatkan pada posisi yang tepat sesuai urutannya. Artinya, kita harus

menyisipkan item baru ditengah-tengah list, di antara dua simpul yang sudah ada.

Page 239: Java With Eclipse

Supaya mudah, kita membutuhkan dua variabel dengan tipe Simpul, di mana masing-

masing menunjuk pada 2 posisi di mana item baru akan disisipkan di antaranya. Pada

ilustrasi berikut, variabel ini adalah sebelum dan pointer. Variabel lain, yaitu

simpulBaru menyimpan referensi ke simpul baru yang akan disisipkan. Untuk melakukan

penyisipan, hubungan antara sebelum dan pointer harus diputus, dan kait baru dari

sebelum ke simpulBaru dan dari simpulBaru ke pointer harus dibuat.

Perintah "sebelum.berikut = simpulBaru;" digunakan untuk membuat

sebelum.berikut menunjuk pada simpul baru. Dan perintah "simpulBaru.berikut =

pointer" digunakan untuk membuat simpulBaru.berikut menunjuk pada pointer.

Akan tetapi, sebelum kita menjalankan perintah ini, kita harus menempatkan posisi

sebelum dan pointer pada tempat yang benar terlebih dahulu (seperti pada ilustrasi di

atas).

Kita akan mulai dari awal list, kemudian pindah ke simpul berikutnya selama isinya lebih

kecil dari item baru. Ketika kita memindahkan pointer dari satu tempat ke tempat

berikutnya, hati-hati bahwa kita bisa sampai di akhir list tanpa kita sadari. Artinya kita

tidak bisa meneruskan lagi apabila pointer sampai ke akhir list, yaitu ketika

pointer.next bernilai null.

Jika sisipItem adalah item item yang akan disisipkan, maka kita asumsikan bahwa item ini

harus berada di dalam list. Kode berikut akan menempatkan posisi sebelum dan pointer

dengan tepat:

Page 240: Java With Eclipse

Simpul pointer, sebelum;

sebelum = kepala; // Mulai di awal list

pointer = kepala.berikut;

while ( pointer != null && pointer.item.compareTo(sisipItem) < 0 ) {

sebelum = pointer; // bisa juga menggunakan "sebelum =

sebelum.berikut"

pointer = pointer.berikut;

}

(Kode di atas menggunakan compareTo() dari kelas String untuk menguji apakah item di

dalam node bernilai kurang dari item yang akan disisipkan.)

Kode di atas boleh-boleh saja, akan tetapi asumsi kita untuk menyisipkan node baru di

tengah-tengah list tidak selamanya benar. Mungkin saja sisipItem lebih kecil dari item

pertama. Artinya, simpul baru harus disisipkan pada kepala list. Ini bisa dilakukan dengan

instruksi berikut :

simpulBaru.berikut = kepala; // Buat simpulBaru.next menunjuk kepala

lama

kepala = simpulBaru; // Buat simpulBaru sebagai kepala list yang

baru

Atau bisa saja listnya kosong. Artinya, simpulBaru menjadi simpul pertama dan satu-

satunya di dalam list. Ini bisa dilakukan dengan mengisi kepala = simpulBaru. Metode

sisip() berikut ini merangkum semua kemungkinan di atas :

public void sisip(String sisipItem) {

// Tambah sisipItem ke dalam list. Boleh menyisipkan

// kopi yang sama

Simpul simpulBaru; // Simpul baru yang berisi item baru

simpulBaru = new Simpul();

simpulBaru.item = sisipItem; // (N.B. isi simpulBaru.berikut masih

null)

if ( kepala == null ) {

// List masih kosong

// Buat kepala menunjuk ke simpulBaru

kepala = simpulBaru;

}

else if ( kepala.item.compareTo(sisipItem) >= 0 ) {

// Item baru kurang dari item pertama list

// Jadi item baru harus disisipkan sebelum kepala list

simpulBaru.berikut = kepala;

Page 241: Java With Eclipse

kepala = simpulBaru;

}

else {

// Item baru akan disisipkan di tengah-tengah list setelah

// item pertama. Cari posisi yang tepat dan sisipkan di sana

Simpul pointer; // Simpul untuk menelusuri list

Simpul sebelum; // Simpul yang menunjuk pada posisi sebelum

pointer

sebelum = kepala; // Set sebelum ke kepala list dan pointer ke

posisi setelahnya

pointer = kepala.berikut;

while (pointer != null && pointer.item.compareTo(sisipItem) < 0)

{

// Pindahkan sebelum dan pointer ke posisi berikutnya hingga

pointer

// sampai di akhir list atau sampai pada item yang isinya

lebih besar

// dari sisipItem. Setelah perulangan selesai, pointer

menunjuk

// pada posisi di mana sisipItem akan disisipkan

sebelum = pointer;

pointer = pointer.berikut;

}

simpulBaru.berikut = pointer; // Sisipkan simpulBaru setelah

"sebelum"

sebelum.berikut = simpulBaru;

}

} // akhir sisip()

Jika Anda memperhatikan dengan seksama diskusi di atas, mungkin Anda akan ingat

bahwa ada satu kasus lagi yang tidak pernah disebutkan. Apa yang terjadi jika simpul baru

harus disisipkan di akhir list? Ini terjadi jika semua item di dalam list lebih kecil daripada

item baru.

Sebenarnya, kasus ini sudah ditangani dengan benar oleh subrutin, yaitu di bagian akhir

pernyataan if . Jika sisipItem lebih besar dari semua item di dalam list, maka perulangan

while akan berhenti ketika pointer selesai menelusuri sampai pada akhir list hingga

pointer bernilai null. Akan tetapi, ketika ini terjadi, sebelum masih tetap menunjuk pada

item terakhir pada list. Perintah sebelum.berikut = simpulBaru menambahkan simpul

baru di akhir list. Karena isi pointer adalah null, maka perintah

Page 242: Java With Eclipse

[code]simpulBaru.berikut = pointer akan mengisi simpulBaru.berikut dengan

null. null adalah nilai yang tepat untuk menandakan akhir list.

Operasi untuk menghapus mirip dengan operasi menyisipkan item, meskupun sedikit lebih

mudah. Masih ada beberapa kasus khusus yang harus dipikirkan. Ketika simpul pertama

akan dihapus, maka isi kepala harus diubah ke simpul kedua. Karena kepala.berikut

adalah simpul berikutnya, maka ini bisa dilakukan dengan perintah kepala =

kepala.berikut. (Sekali lagi, perhatikan bahwa perintah ini juga berlaku jika

kepala.berkut berisi null[code], yaitu ketika hanya ada satu item di dalam

list. Ketika satu-satunya item ini dihapus, maka list bernilai [code]null

yang artinya list sudah kosong.)

Jika simpul yang akan dihapus ada di tengah-tengah list, maka kita bisa membuat variabel

sebelum dan pointer di mana pointer menunjuk pada simpul yang akan dihapus, dan

sebelum menunjuk pada simpul sebelumnya. Setelah diposisikan dengan benar, perintah

"sebelum.berikut = pointer.berikut" akan menghapus simpul tersebut. Simpul yang

dihapus akan diambil oleh pemulung memori.

Berikut ini adalah kode lengkap dari metode hapus() :

public boolean hapus(String hapusItem) {

// Jika hapusItem ada dalam list, hapus.

// Kembalikan true jika string ditemukan dan dihapus.

// Jika string tidak ditemukan kembalikan false.

// (Jika ada beberapa item dengan isi yang sama, hanya

// hapus yang pertama)

if ( kepala == null ) {

// Jika list kosong, sudah pasti tidak ada string hapusItem

return false;

}

else if ( kepala.item.equals(hapusItem) ) {

// Jika hapusItem ada pada simpul pertama, hapus.

kepala = kepala.berikut;

return true;

}

else {

// Di sini, maka ada kemungkinan string terdapat

// di tengah-tengah list. Cari itemnya di dalam list.

Simpul pointer; // Simpul untuk menelusuri list

Page 243: Java With Eclipse

Simpul sebelum; // Selalu menunjuk pada simpul sebelum pointer

sebelum = kepala; // Mulai dari awal list

pointer = kepala.berikut;

while (pointer != null && pointer.item.compareTo(hapusItem) < 0)

{

// Pindahkan sebelum dan pointer di dalam list hingga

// pointer sampai pada akhir list atau sampai pada item yang

// lebih besar atau sama dengan hapusItem. Ketika perulangan

// selesai, pointer menunjuk pada posisi di mana hapusItem

// seharusnya berada (jika ada)

sebelum = pointer;

pointer = pointer.berikut;

}

if ( pointer != null && pointer.item.equals(hapusItem) ) {

// Pointer menunjuk pada simpul yang akan dihapus

// Hapus dengan mengubah simpul sebelumnya

sebelum.berikut= pointer.berikut;

return true;

}

else {

// Item yang dicari tidak ada

return false;

}

}

} // akhir hapus()

Tumpukan

List berantai adalah salah satu jenis struktur data, yang tersusun dari objek yang terkait

satu sama lain oleh pointer. Pada bagian sebelumnya, kita menggunakan list berantai untuk

menyimpan String terurut, dan kita juga mengimplementasikan operasi sisip, hapus dan

cari pada list tersebut.

Akan tetapi kita juga bisa menyimpan list String pada array atau ArrayList. Kita bisa juga

mengimplementasikan operasi sisip, hapus, dan cari. Implementasi operasi tersebut

akan berbeda, akan tetapi antar muka dan perilakunya akan tetap sama.

Istilah tipe data abstrak (abstract data type, atau ADT) adalah kumpulan nilai dan operasi

yang bisa dilakukan pada nilai tersebut, tanpa perlu mengetahui bagaimana nilai tersebut

disimpan dan bagaimana operasi tersebut diimplementasikan.

Page 244: Java With Eclipse

Suatu "list terurut yang berisi string" adalah contoh tipe data abstrak. Ada banyak cara

untuk mengimplementasikan tipe data abstrak yang sama, misalnya seperti disebut di atas,

list terurut berisi string bisa diimplementasikan dalam bentuk list berantai atau array.

Program yang menggunakan tipe data ini bisa menggunakannya tanpa mengetahui dengan

detail tentang implementasinya. Lebih jauh, implementasi TDA bisa diganti tanpa

mempengaruhi jalannya program secara keseluruhan. Dengan cara ini, program bisa lebih

mudah untuk dipelihara dan didebug. TDA adalah alat penting dalam rekayasa perancang

lunak.

Pada bagian ini dan yang akan datang, kita akan lihat TDA lain, yaitu tumpukan dan

antrian. Tumpukan dan antrian sering diimplementasikan dalam bentuk list berantai, akan

tetapi ini bukan satu-satunya cara implementasi. Mari kita anggap bagian ini sebagai studi

kasus dari TDA.

Tumpukan (stack) terdiri dari kumpulan item yang disusun sedemikian rupa sehingga satu

item ditumpuk di atas item lainnya, mirip seperti tumpukan boks. Hanya item paling atas

yang bisa diakses pada suatu saat.

Item tersebut bisa diambil dari tumpukan dengan operasi yang disebut "ambil" (atau dalam

bahasa Inggris, istilah untuk mengeluarkan item dari tumpukan disebut "pop"). Item di

bawah hanya bisa diambil jika semua item di atasnya telah dikeluarkan dari tumpukan.

Suatu item hanya bisa ditambahkan di atas tumpukan dengan perintah "dorong" (atau

"push").

Kita bisa membuat tumpukan dari berbagai macam data. Misalnya, jika itemnya bertipe

int, maka operasi dorong dan keluar bisa diimplementasikan dalam metode instansi

void dorong(int itemBaru) // Tambah itemBaru di atas tumpukan

int ambil() // Mengambil int paling atas pada tumpukan

Kita tidak bisa mengambil item dari tumpukan yang kosong, jadi kita juga perlu memberi

tahu apakah suatu tumpukan kosong atau tidak. Kita perlu operasi lain untuk mengujinya,

yang diimplementasikan dalam metode instansi

boolean isKosong() // Kembalikan true jika tumpukan kosong

Ilustrasi berikut menggambarkan tumpukan int, sebagai tipe data abstrak. TDA ini bisa

diimplementasikan dengan berbagai cara, akan tetapi gambar tumpukan di dalam imaginasi

kita akan tetap sama.

Page 245: Java With Eclipse

Pada implementasi dengan list berantai, tumpukan atas adalah simpul kepala dari list. Kita

bisa menambah dan mengurangi simpul pada kepala list berantai -- jauh lebih mudah

daripada menyisipkan atau menghapus simpul dari tengah-tengah list.

Berikut ini adalah kelas "tumpukan int" yang mengimplementasikan TDA menggunakan

list berantai. (Kelas ini menggunakan kelas bertingkat sebagai kelas simpul dari list

berantai. Lihat bagian sebelumnya tentang kelas bertingkat. Jika Anda masih belum paham

atau merasa tidak nyaman menggunakan kelas bertingkat, Anda bisa juga memisahkannya

sebagai kelas terpisah.)

public class TumpukanInt {

private static class Simpul {

// Objek dengan tipe Simpul menyimpan

// satu item di dalam list berantai

int item;

Simpul berikut;

}

// Referensi ke simpul paling atas dari tumpukan

// jika atas == null, maka tumpukan tidak berisi

// apa-apa (kosong)

private Simpul atas;

Page 246: Java With Eclipse

public void dorong( int N ) {

// Letakkan N di tumpukan paling atas

Simpul atasBaru; // Simpul baru untuk menyimpan item baru

atasBaru = new Simpul();

atasBaru.item = N; // Simpan N di simpul baru

atasBaru.berikut = atas; // Simpul baru menunjuk pada atas lama

atas = atasBaru; // Simpul baru sekarang menjadi atas

}

public int ambil() {

// Ambil item paling atas dari dalam tumpukan

// dan kembalikan nilainya. Catatan bahwa rutin ini akan

// melempar NullPointerException jika kita mencoba untuk

// mengambil item dari tumpukan kosong

int itemAtas = atas.item; // Item yang akan diambil

atas = atas.berikut; // Item yang dulunya di posisi kedua

sekarang di atas

return itemAtas;

}

public boolean isKosong() {

// Kembalikan true jika tumpukan kosong.

// Kembalikan false jika ada satu atau lebih item di dalam

tumpukan

return (atas == null);

}

} // akhir kelas TumpukanInt

Anda perlu memahami bagaimana operasi dorong dan ambil dilaksanakan. Mungkin

sedikit oret-oretan akan membantu. Lihat bahwa list berantai merupakan bagian private

dari kelas TumpukanInt. Program yang menggunakan kelas ini tidak perlu tahu bahwa list

berantai digunakan dalam implementasinya.

Sekarang, kita bisa juga mengimplementasikan tumpukan dalam bentuk array, bukan

hanya list berantai. Karena banyaknya item di dalam tumpukan bervariasi dan tidak bisa

ditentukan, maka kita perlu memiliki variabel lain yang melacak berapa banyak tempat

dalam array yang sudah digunakan.

Jika variabel ini kita namakan atas, maka item akan disimpan dalam tumpukan pada

posisi 0, 1, ..., atas - 1. Item pada posisi 0 adalah item paling bawah dalam tumpukan,

sedangkan item atas - 1 adalah item paling atas.

Page 247: Java With Eclipse

Untuk mendorong item baru ke dalam tumpukan, kita bisa meletakkan item di posisi atas

kemudian nilai atas ditambahkan 1. Jika kita tidak ingin memberi limit banyaknya item di

dalam tumpukan, kita bisa menggunakan array dinamis yang telah dibahas sebelumnya.

Berikut ini adalah implementasi kelas TumpukanInt dengan menggunakan array dinamis:

public class TumpukanInt {

private int[] item = new int[10]; // Menyimpan item dalam tumpukan

private int atas = 0; // Banyaknya item dalam tumpukan

public void dorong( int N ) {

// Tambahkan N ke dalam tumpukan

if (atas == item.length) {

// Array sudah penuh, jadi buat array yang lebih besar dan

// kopi isi tumpukan sekarang ke dalam array baru

int[] arrayBary = new int[ 2*item.length ];

System.arraycopy(item, 0, arrayBary, 0, item.length);

item = arrayBaru;

}

item[atas] = N; // Letakkan N di tempat kosong berikutnya

atas++; // Jumlah item bertambah 1

}

public int ambil() {

// Mengambil item teratas dari tumpukan dan mengembalikannya

// Ingat bahwa rutin ini akan melempar pengecualian

// ArrayIndexOutOfBoundsException jika mencoba mengambil

// item dari tumpukan kosong

int itemAtas = item[top - 1] // Item teratas di dalam tumpukan

atas--; // Jumlah item dalam tumpukan berkurang 1

return itemAtas;

}

public boolean isKosong() {

// Kembalikan true jika tumpukan kosong

// Kembalikan false jika ada satu atau lebih item di dalam

tumpukan

return (atas == 0);

}

Page 248: Java With Eclipse

} // akhir kelas TumpukanInt

Sekali lagi, implementasi tumpukan (dalam bentuk array) bersifat private. Kedua versi

kelas TumpukanInt bisa digunakan dalam suatu program. Jika suatu program

menggunakan versi pertama, maka kita bisa menggantinya dengan versi kedua tanpa

mengubah isi program lain. Akan tetapi, ada satu kasus di mana kedua kelas akan berbeda,

yaitu jika mencoba mengambil item dari tumpukan kosong, maka versi pertama akan

melemparkan NullPointerException sedangkan versi kedua akan melemparkan

ArrayIndexOutOfBoundsException.

Mungkin akan lebih baik untuk mendefinisikan jenis pengecualian baru, misalnya

EmptyStackException di kedua versi. Dan sebetulnya TDA harusnya memberikan

spesifikasi apa yang harus dilakukan jika program mencoba mengambil item dari

tumpukan kosong. Ini yang sering dilupakan ketika seseorang membuat spesifikasi untuk

interface, yang akhirnya masalah lain akan muncul di kemudian hari.

Apa contoh kegunaan tumpukan dalam keadaan sehari-hari? Misalnya, lihat apa yang

terjadi jika suatu rutin memanggil subrutin. Rutin pertama akan dihentikan sementara

ketika subrutin yang dipanggil dieksekusi, dan akan diteruskan apabila subrutin yang

dipanggil selesai dijalankan.

Sekarang, misalnya subrutin tersebut memanggil subrutin kedua, dan subrutin kedua

memanggil subrutin ketiga, dan seterusnya. Setiap subrutin akan berhenti untuk sementara

ketika subrutin berikutnya dipanggil. Komputer harus bisa melacak subrutin yang

dihentikan. Bagaimana caranya? Yaitu dengan menggunakan tumpukan.

Ketika subrutin dipanggil, catatan aktivasi (activation record) dibuat untuk subrutin

tersebut. Catatan aktivasi ini berisi informasi yang relevan tentang eksekusi dari subruti

tersebut, misalnya parameter dan variabel lokalnya. Catatan aktivasi ini ditempatkan dalam

tunpukan. Catatan ini akan dibuang ketika subrutin selesai dipanggil.

Jika subrutin ini memanggil subrutin lain, maka catatan aktivasi dari subrutin kedua akan

didorong ke dalam tumpukan, di atas catatan aktivasi subrutin pertama. Tumpukan akan

semakin besar jika semakin banyak subrutin yang dipanggil, dan semakin kecil jika

subrutin-subrutin itu selesai dijalankan.

Contoh lainnya, tumpukan digunakan untuk mengevaluasi ekspresi postfix (postfix

expresssion). Ekspresi matematika biasa seperti 2+(15-12)*17 disebut ekspresi infix.

Dalam ekspresi infix, operator berada di tengah nilai yang akan dihitung, misalnya "2 +

2". Dalam ekspresi postfix, operator ada di akhir nilai, misalnya "2 2 +".

Page 249: Java With Eclipse

Ekspresi 2+(15-12)*17" dapat ditulis dalam bentuk postfix menjadi "2 15 12 - 17 *

+". Di sini operator "-" dilakukan pada dua nilai sebelumnya, yaitu "15" dan "12". Tanda *

dilakukan pada dua nilai sebelumnya, yaitu "15 12 -" dan "17". Dan operator "+" dilakukan

pada 2 dan "15 12 - 17 *". Hasilnya akan persis sama dengan ekspresi infix awalnya.

Meskipun lebih mudah bagi manusia untuk melakukan perhitungan dengan ekspresi infix,

ekspresi postfix memiliki beberapa keuntungan. Satu hal, ekspresi postfix tidak

memerlukan tanda kurung atau aturan perhitungan (tanda kali harus dilakukan lebih dulu

sebelum tambah misalnya). Aturan penghitungan hanya ditentukan berdasarkan urutannya

saja. Sehingga algoritma yang menghitung ekspresi postfix dapat menjalankannya dengan

lebih cepat dan tepat.

Sekarang misalnya kita akan menghitung ekspresi "2 15 12 - 17 * +" dari kiri ke kanan.

Nilai yang kita temui pertama adalah 2, tapi apa yang bisa kita lakukan dengan 2? Di sini

kita belum tahu operatornya, dan selain itu kita juga belum tahu nilai lainnya. Kita akan

ingat nilai 2 ini untuk sementara, yaitu dengan mendorongnya ke dalam tumpukan.

Berikutnya, kita akan melihat nilai 15, yang kita juga masukkan ke dalam tumpukan di atas

nilai 2. Kemudian nilai 12 juga dimasukkan ke dalam tumpukan di atas 15.

Sekarang kita sampai pada operator "-". Operasi ini dilakukan pada dua nilai sebelumnya.

Kita telah menyimpan 2 nilai sebelumnya ke dalam tumpukan, yaitu 15 dan 12. Sekarang

kita ambil 2 nilai tersebut dari dalam tumpukan, dan kita lakukan perhitungan 15 - 12

yaitu 3.

Nilai 3 ini kita simpan lagi ke dalam tumpukan. Ingat bahwa 15 dan 12 sudah diambil dari

dalam tumpukan, sehingga hanya nilai 2 yang ada di dalam tumpukan. Setelah nilai 3

dimasukkan ke dalam tumpukan, maka 3 ada di atas 2 di dalam tumpukan.

Item berikutnya adalah 17, yang juga dimasukkan ke dalam tumpukan di atas nilai 3.

Untuk menghitung item berikutnya "*", kita ambil 2 nilai dari dalam tumpukan, yaitu 3

dan 17. Hasil dari 3 * 17, yaitu 51 dimasukkan kembali ke dalam tumpukan (di atas 2

yang masih ada di dalam tumpukan).

Item berikutnya adalah "+", yang akan mengambil 51 dan 2 dari dalam tumpukan,

menghitung hasilnya, yaitu 53, kemudian menyimpannya lagi ke dalam tumpukan.

Sekarang kita sampai pada akhir ekspresi. Nilai pada tumpukan itu adalah hasil

perhitungan keseluruhan ekspresi. Kita cukup mengambil nilainya dan melaporkan

hasilnya, yaitu 53.

Algoritma untuk melakukan perhitungan ekspresi postfix adalah sebagai berikut :

Mulai dengan tumpukan kosong

Page 250: Java With Eclipse

untuk setiap item di dalam ekspresi:

jika item berupa bilangan:

Dorong item ke dalam tumpukan

jika item berupa operator

Ambil dua nilai dari tumpukan // bisa terjadi kesalahan

Lakukan perhitungan dua nilai dengan operator tersebut

Dorong hasil perhitungan ke dalam tumpukan

else

Ada kesalahan dalam ekspresi

Ambil nilai dari tumpukan

Jika tumpukan tidak kosong:

Ada kesalahan dalam ekspresi

else:

Nilai terakhir adalah hasil perhitungan

Kesalahan ekspresi dapat dideteksi dengan mudah. Misalnya, dalam ekspresi "2 3 + *",

tidak cukup nilai untuk menghitung operasi "*". Ini akan dideteksi oleh algoritma ketika

mencoba mengambil nilai kedua dari dalam tumpukan, karena pada saat itu tumpukan

sudah kosong.

Kesalahan lain bisa juga muncul ketika menghitung ekspresi "2 3 4 +", di mana tidak

cukup operator untuk menghitung semua nilai. Ini akan dideteksi ketika 2 masih ada di

dalam tumpukan di akhir algoritma.

Ekspresi postfix sering digunakan secara internal oleh komputer. Dan sebenarnya, mesin

virtual Java adalah "mesin tumpukan", yang menggunakan tumpukan untuk melakukan

perhitungan yang telah kita diskusikan. Algoritma ini bisa diperluas untuk menangani

variabel dan konstanta. Ketika variabel ditemukan di dalam ekspresi, isi variabel ini

didorong ke dalam tumpukan.

Algoritma ini juga bisa dikembangkan untuk operator yang membutuhkan kurang atau

lebih dari dua operator. Banyaknya nilai yang diambil dari dalam tumpukan bisa

disesuaikan dengan berapa banyak nilai yang dibutuhkan. Misalnya, operator "-" sebagai

operator negasi, misalnya "-x" hanya membutuhkan satu nilai.

Antrian

Antrian (queue) adalah struktur data mirip dengan tumpukan, yaitu terdiri dari item dalam

urutan tertentu. Antrian memiliki dua ujung, yang disebut ujung depan dan ujung belakang.

Item selalu dimasukkan dari belakang antrian, dan selalu dikeluarkan dari depan antrian.

Operasi memasukkan dan mengeluarkan item dari dalam antrian disebut "masuk" dan

"keluar" (dalam bahasa Inggris disebut enqueue dan dequeue).

Page 251: Java With Eclipse

Suatu item yang ditambahkan di belakang antrian tidak bisa dihapus sebelum item di

depannya dihapus. Mirip seperti antrian pada kasir atau pada customer service di bank

misalnya. Customer akan dilayani dalam urutan ketika mereka datang.

Antrian bisa menampung item dengan jenis apa saja. Untuk antrian int, operasi masuk dan

keluar dapat diimplementasikan sebagai metode instansi dalam kelas "AntrianInt". Kita

juga membutuhkan metode instansi untuk menguji apakah antrian kosong atau tidak:

void masul(int N) // Tambah N di belakang antrian

int keluar() // Keluarkan antrian dari depan dan kembalikan isinya

boolean isKosong() // Kembalikan true jika antrian kosong

Antrian bisa diimplementasikan dalam bentuk list berantai atau array. Akan tetapi

implementasi yang efisien dalam bentuk array sedikit lebih sulit, sehingga akan kita

abaikan dalam diskusi kita.

Dalam implementas dalam bentuk list berantai, item bertama adalah item di depan antrian.

Untuk mengeluarkan item dari depan antrian, kita dapat lakukan seperti mengambil item

dari atas tumpukan.

Page 252: Java With Eclipse

Belakang antrian adalah akhir dari list. Unutk memasukkan item ke dalam antrian, kita

harus mengeset pointer di akhir simpul untuk menunjuk ke simpul baru yang berisi item

yang akan kita tambahkan. Kita bisa menggunakan perintah seperti "buntut.berikut =

simpulBaru;", di mana buntut adalah pointer ke simpul terakhir dari list.

Jika kepala adalah pointer ke simpul pertama, maka kita bisa mendapat pointer ke simpul

terakhir dengan menggunakan :

Simpul buntut; // Pointer ini akan menunjuk ke simpul terakhir

buntut = kepala; // Mulai dari simpul pertama

while (buntut.berikut != null) {

buntut = buntut.berikut;

}

// Di sini, buntut.berikut berisi null, artinya buntut adalah

// simpul terakhir di dalam list

Akan tetapi, tentunya akan sangat tidak efisien jika kita harus melakukan perulangan terus

menerus setiap kali kita memasukkan item baru ke dalam antrian. Dengan alasan efisiensi,

kita akan menyimpan pointer ke akhir simpul sebagai variabel instansi. Kita harus selalu

ingat untuk terus mengupdate isi variabel ini setiap kali simpul baru ditambahkan ke akhir

list.

Dengan pengetahuan di atas, kita bisa membuat kelas "AntrianInt" dengan mudah

sebagai berikut :

public class AntrianInt {

private static class Simpul {

// Objek bertipe Simpul berisi item dalam bentuk

// list berantai

int item;

Simpul berikut;

}

// Menunjuk ke simpul pertama pada antrian

// Jika antrian kosong, maka kepala berisi null

private Simpul kepala = null;

private Simpul buntut = null; // Menunjuk ke simpul akhir pada

antrian

void masuk( int N ) {

// Menambahkan N ke akhir antrian

Page 253: Java With Eclipse

Simpul buntutBaru = new Simpul(); // Simpul baru untuk menampung

item baru

buntutBaru.item = N;

if (kepala == null) {

// Antrian kosong, sehingga simpulBaru menjadi

// satu-satunya simpul di dalam list. Sehingga

// kepala dan buntut sama-sama menunjuk ke simpulBaru

kepala = buntutBaru;

buntut = buntutBaru;

}

else {

// Simpul baru menjadi buntut antrian

// (kepala tidak berpengaruh apa-apa)

buntut.berikut = buntutBaru;

buntut = buntutBaru;

}

}

int keluar() {

// Keluarkan item dari kepala antrian

// Bisa melempar NullPointerException.

int itemPertama = kepala.item;

kepala = kepala.berikut; // Sekarang item kedua menjadi kepala

if (kepala == null) {

// Sekarang antrian kosong. Simpul yang telah dihapus adalah

// kepala sekaligus buntut, karena simpul ini adalah satu-

satunya

// yang ada di dalam antrian. Isi buntut dengan null.

buntut = null;

}

return itemPertama;

}

boolean isKosong() {

// Kembalikan true jika antrian kosong

return (kepala == null);

}

} // akhir kelas AntrianInt

Page 254: Java With Eclipse

Antrian digunakan dalam komputer (dan juga dalam kehidupan nyata) ketika hanya satu

item yang bisa diproses pada suatu saat, akan tetapi banyak item bisa menunggu untuk

diproses, misalnya :

Dalam Java program yang memiliki banyak threads, thread yang ingin diolah dalam

CPU disimpan di dalam antrian. Ketika thread baru dimulai, thread ini ditambahkan

ke dalam antrian. Thread akan dihapus dari depan antrian, diolah oleh CPU, dan

kemudian -- jika belum selesai -- diletakkan kembali di belakang antrian untuk

menunggu giliran berikutnya.

Event seperti ketikan tombol dan klik mouse disimpan dalam antrian yang disebut

"antrian event". Program akan menghapus item dari antrian item dan mengolahnya

satu per satu. Mungkin saja terjadi beberapa event diterima ketika satu event

sedang diproses, akan tetapi event akan diolah berdasarkan urutan kedatangannya

ServerSocket, memiliki antrian di dalamnya yang berisi permintaan sambungan

yang diterima akan tetapi belum dilayani. Metode accept() pada kelas

ServerSocket menerima permintaan sambungan berikutnya dari depan antrian ini.

Antrian disebut mengikuti kebijakan FIFO (First In First Out, atau Pertama Masuk Pertama

Keluar). Atau dalam bahasa awam, pertama datang dan pertama dilayani. Di lain pihak,

tumpukan mengikuti prinsip LIFO (Last In First Out, Terakhir Masuk Pertama Keluar).

Item yang bisa keluar dari tumpukan adalah item terkakhir yang dimasukkan. Seperti

antrian, tumpukan juga bisa digunakan untuk menampung item yang sedang menunggu

untuk diproses (walaupun dalam aplikasi di mana antrian biasa digunakan, tumpukan

adalah antrian yang tidak adil).

Pohon Biner

Kita telah melihat di beberapa bagian sebelumnya bagaimana objek bisa dikaitkan satu

sama lain menjadi list berantai. Ketika suatu objek memiliki 2 pointer ke objek dengan tipe

yang sama, kita bisa membuat struktur data yang lebih kompleks dari list berantai. Dalam

bagian ini kita akan melihat salah satu struktur dasar dan paling berguna: pohon biner

(binary tree).

Setiap objek dalam pohon biner memiliki dua pointer, yang biasa disebut kiri dan kanan.

Selain itu, tentunya simpul pada pohon memiliki data yang bisa bertipe apa saja. Misalnya,

pohon biner integer bisa dibuat dalam bentuk :

class SimpulPohon {

int item; // Data pada simpul ini

Page 255: Java With Eclipse

SimpulPohon kiri; // Pointer ke cabang sebelah kiri

SimpulPohon kanan; // Pointer ke cabang sebelah kanan

}

Pointer kiri dan kanan dalam SimpulPohon bisa beriis null atau menunjuk pada objek

lain dengan tipe SimpulPohon. Simpul yang menunjuk pada simul lain disebut induk

(parent), sedangkan yang ditunjuk disebut anak (child). Dalam gambar di atas, simpul 3

adalah induk dari simpul 6, dan simpul 4 dan 5 adalah anak dari simpul 2.

Tidak semua struktur yang terdiri dari simpul pohon merupakan pohon biner. Pohon biner

harus memiliki sifat :

Harus ada satu simpul di dalam pohon yang tidak memiliki induk. Simpul ini

disebut simpul akar (root).

Simpul lain harus memiliki hanya satu induk

Tidak boleh ada perulangan dalam pohon biner, artinya tidak boleh ada rantai

pointer yang dimulai dari satu simpul dan berakhir di simpul yang sama.

Simpul yang tidak memiliki anak disebut simpul daun (leaf). Simpul daun dapat dikenali

karena kedua pointer kiri dan kanannya berisi null. Dalam ilustrasi suatu pohon biner,

biasanya simpul akar terletak di atas dan simpul daun terletak di bawah -- tidak sama

seperti pohon sungguhan. Akan tetapi, kita bisa melihat cabang-cabang, seperti pohon,

yang merupakan cikal bakal nama pohon biner ini.

Page 256: Java With Eclipse

Misalnya, mari kita lihat suatu simpul pada pohon biner. Lihat simpul tersebut beserta

seluruh simpul turunannya (yaitu anak, anak dari anaknya, dan seterusnya). Simpul-simpul

ini juga membentuk pohon biner, yang disebut pohon cabang (subtree) dari pohon

awalnya. Misalnya, pada gambar di atas, simpul 2, 4, dan 5 membentuk pohon cabang.

Pohon cabang ini disebut pohon cabang sebelah kiri dari simpul akarnya. Hal yang sama,

simpul 3 dan 6 adalah pohon cabang sebelah kanan dari simpul akarnya. Salah satu atau

kedua pohon cabang bisa kosong. Ini adalah definisi rekursif. Jadi tidak heran apabila kita

menerapkan subrutin rekursif untuk mengolah struktur data pohon.

Mari kita lihat bagaimana caranya menghitung banyaknya simpul di dalam pohon biner.

Sebagai latihan, kita mungkin bisa membuat algoritma untuk menghitung simpul. Inti

permasalahannya adalah, bagaimana melacak simpul mana yang belum dihitung. Ini bukan

hal sepele. Dan mungkin kita tidak mungkin menyelesaikannya tanpa menggunakan

tumpukan atau antrian.

Dengan rekursi, algoritmanya akan jauh lebih mudah. Suatu pohon bisa kosong, atau bisa

terdiri dari akar dan dua pohon cabang. Jika pohon kosong, maka banyaknya simpul adalah

nol. (Ini merupakan kasus dasar dari rekursi). Kemudian kita bisa menggunakan rekursi

untuk menghitung jumlah simpul pada masing-masing pohon cabang. Jumlahkan hasil dari

kedua cabang, kemudian tambah satu simpul akarnya. Ini adalah jumlah simpul di dalam

pohon.

Dalam Java bisa kita tuliskan sebagai :

static int hitungSimpul( SimpulPohon akar ) {

// Hitung berapa simpul yang dimiliki suatu pohon

// biner, termasuk akarnya

if ( akar == null )

return 0; // Pohon kosong, tidak ada simpul di dalamnya

else {

int jumlah = 1; // Mulai dengan menghitung akarnya

// Tambahkan dengan simpul dari pohon cabang sebelah kiri

jumlah += hitungSimpul(akar.kiri);

// Tambahkan dengan simpul dari pohon cabang sebelah kanan

jumlah += hitungSimpul(akar.kanan);

return hitung; // Kembalikan jumlahnya

}

} // akhir hitungSimpul()

Page 257: Java With Eclipse

Juga, mari kita lihat bagaimana mencetak isi item di dalam pohon biner. Jika pohon

kosong, kita tidak melakukan apa-apa. Jika pohon tidak kosong, maka mungkin ia berisi

akar dan dua pohon cabangnya. Cetak item pada akarnya, dan gunakan rekursi untuk

mencetak item di dalam pohon cabangnya. Berikut ini adalah subrutin untuk mencetak

semua item dalam satu baris cetakan.

static void preorderCetak( SimpulPohon akar ) {

// Cetak semua item di dalam pohon yang ditunjuk oleh akar.

// Item pada akar dicetak dahulu, kemudian item di sebelah kiri

// dan baru item di pohon cabang sebelah kanan

if ( akar != null ) { // (Jika null, kita tidak melakukan apa-apa.)

System.out.print( akar.item + " " ); // Print item akar

preorderCetak( akar.kiri ); // Print item di pohon cabang kiri

preorderCetak( akar.kanan ); // Print items di pohon cabang

kanan

}

} // akhir preorderCetak()

Rutin ini disebut "preorderCetak" karena ia menelusuri pohon secara preorder. Dalam

penelusuran preorder, simpul akan diproses terlebih dahulu, kemudian pohon cabang

sebelah kiri ditelusuri, dan kemudian cabang sebelah kanan. Dalam penelusuran postorder,

cabang kiri ditelusuri, kemudian cabang kanan, baru kemudian simpul akar. Dan dalam

penelusuran inorder, cabang kiri dikunjungi dahulu, kemudian akar, dan kemudian cabang

kanan.

Subrutin yang mencetak postorder dan inorder berbeda dengan preorder dalam urutan

pencetakannya di layar :

static void postorderCetak( SimpulPohon akar ) {

// Cetak semua item di dalam pohon yang ditunjuk oleh akar.

// Cabang pohon sebelah kiri dicetak dahulu, kemudian kanan,

// dan baru simpul akarnya

if ( akar != null ) { // (Jika null, kita tidak melakukan apa-apa.)

postorderCetak( akar.kiri ); // Print item di pohon cabang kiri

postorderCetak( akar.kanan ); // Print items di pohon cabang

kanan

System.out.print( akar.item + " " ); // Print item akar

}

} // akhir postorderCetak()

static void inorderCetak( SimpulPohon akar ) {

// Cetak semua item di dalam pohon yang ditunjuk oleh akar.

Page 258: Java With Eclipse

// Cabang pohon cabang sebelah kiri dicetak dahulu,

// kemudian simpul akarnya, baru pohon cabang sebelah kanan

if ( akar != null ) { // (Jika null, kita tidak melakukan apa-apa.)

inorderCetak( akar.kiri ); // Print item di pohon cabang kiri

System.out.print( akar.item + " " ); // Print item akar

inorderCetak( akar.kanan ); // Print items di pohon cabang kanan

}

} // akhir inorderCetak()

Subrutin ini bisa dijalankan pada pohon biner yang diilustrasikan di awal bagian ini.

Urutan item ketika dicetak akan berbeda, di mana :

preorderCetak mencetak: 1 2 4 5 3 6

postorderCetak mencetak: 4 5 2 6 3 1

inorderCetak mencetak: 4 2 5 1 3 6

Dalam preorderCetak misalnya, itam pada akar pohon, yaitu 1, dicetak paling awal. Akan

tetapi urutan preorder juga dilakukan untuk setiap pohon cabangnya. Simpul akar pada

pohon cabang sebelah kiri, yaitu 2, dicetak terlebih dahulu sebelum pohon cabangnya 4

dan 5. Sedangkan untuk cabang sebelah kanan, akarnya 3 akan dicetak sebelum 6.

Penelusuran preorder dilakukan pada semua cabang pohon. Urutan penelusuran yang

lainnya bisa dianalisis dengan cara yang sama.

Pohon Pengurutan Biner

Pada bagian sebelumnya kita membahas tentang list berantai dari string, di mana string

dijaga agar tetap dalam urutan menaik. Akan tetapi list berantai seperti ini bisa bekerja

untuk jumlah yang tidak terlalu banyak. Untuk jumlah item yang sangat banyak, list

berantai kurang efisien. Ketika kita menambahkan item ke dalam list, kita harus mencari

dahulu posisi yang tepat di mana item akan disisipkan. Untuk melakukan pencarian, kita

harus melihat paling tidak separuh dari seluruh list.

Jika string list diimplentasikan dalam bentuk array terurut, maka pencariannya bisa

dilakukan lebih cepat, karena pencarian biner bisa digunakan. Akan tetapi untuk

menyisipkan item ke dalam array terurut juga tidak efisien, karena kita harus

memindahkan paling tidak setengah isi array untuk memberi tempat untuk item baru yang

akan disisipkan.

Pohon biner bisa digunakan untuk menyimpan string list terurut (atau item jenis lain),

sehingga pencarian dan penyisipan bisa dilakukan dengan mudah. Pohon biner ini disebut

pohon pencarian biner.

Page 259: Java With Eclipse

Pohon pencarian biner adalah pohon biner yang memiliki sifat :

Untuk setiap simpul pada pohon, item pada simpul tersebut lebih besar dari semua

item di cabang kiri pohon

Dan simpul tersebut, lebih besar atau sama dengan semua item di cabang kanan

pohon

Berikut ini adalah contoh pohon pengurutan biner yang memiliki item bertipe String.

(Dalam gambar ini pointer berisi null tidak digambarkan, sedangkan pointer yang tidak

null dilambangkan dengan tanda panah)

Pohon pencarian biner memiliki sifat penting berikut : Penelusuran inorder akan mengolah

item dalam urutan menaik. Misalnya, penelusuran inorder digunakan untuk mencetak isi

pohon di atas dalam urutan alfabet. Penelusuran inorder menjamin bahwa semua item di

cabang kiri pohon dari elemen "judy" akan dicetak sebelum "judy", dan semua item di

cabang kanan pohon akan dicetak setelah "judy". Karena sifat pohon biner yang

mengharuskan item di cabang kiri lebih kecil dari item pada simpul, maka keluarannya

akan sama dengan mengurutkan isi pohon secara alfabet dalam urutan menaik.

Misalnya kita ingin mencari item di dalam pohon pencari biner. Pertama, bandingkan item

dengan isi simpul akarnya. Jika isinya sama, maka kita selesai. Jika item yang kita cari

kurang dari item pada simpul akar, kita harus mencari ke sebelah kiri pohon -- pohon

sebelah kanan bisa diabaikan karena isinya hanya item yang lebih besar dari simpul

akarnya. Demikian juga jika item yang kita cari lebih besar dari item pada simpul akar,

maka kita akan mencari di sebelah kanan pohon. Untuk semua kasus, kita bisa

menggunakan prosedur yang sama di setiap cabang pohon.

Page 260: Java With Eclipse

Bagaimana dengan memasukkan item baru ke dalam pohon. Mula-mula cari di posisi mana

item tersebut akan dimasukkan. Jika posisinya ditemukan, buat simpul baru dan tempelkan

simpul baru di tempat tersebut.

Pencarian dan penyisipan item adalah operasi yang efisien pada pohon pencarian biner,

asalkan pohon tersebut berada dalam kondisi seimbang (balanced). Suatu pohon biner

berada dalam kondisi seimbang jika jumlah simpul pada cabang kanan sama dengan

jumlah simpul pada cabang kiri. Tidak semua pohon biner akan menjadi pohon seimbang,

akan tetapi jika pohon ini dibuat secara acak, besar kemungkinan pohon tersebut akan

menjadi seimbang.

Dalam pencarian dalam pohon pencarian biner, setiap pengujian yang kita lakukan akan

membuang sebagian cabang pohon. Jika pohon tersebut dalam keadaan seimbang, maka

akan semakin banyak elemen yang kita buang, dan dengan demikian pencarian akan

dilakukan dengan lebih cepat. Ini mirip sekali dengan algoritma pencarian biner pada

bagian sebelumnya.

Mari kita lihat bagaimana mengimplementasikan pohon pencarian biner.

Simpul pada pohon biner diekspresikan dalam kelas SimpulPohon berikut ini, beserta

konstruktor untuk membuat simpul baru lebih mudah.

class SimpulPohon {

// Objek SimpulPohon adalah satu simpul pada

// pohon biner string

String item; // Data pada simpul ini

SimpulPohon kiri; // Pointer ke cabang kiri

SimpulPohon kanan; // Pointer ke cabang kanan

SimpulPohon(String str) {

// Konstruktor. Membuat simpul berisi str

item = str;

}

} // akhir kelas SimpulPohon

Variabel statik dengan tipe SimpulPohon menunjuk pada pohon pencarian biner:

// Pointer ke simpul akar pohon

// Ketika pohon kosong, akar berisi null

static SimpulPohon akar;

Subrutin rekursif bernama pohonBerisi digunakan untuk mencari item di dalam pohon.

Subruin berikut melakukan pencarian pada pohon biner seperti didiskusikan di atas.

static boolean pohonBerisi( SimpulPohon simpul, String item ) {

Page 261: Java With Eclipse

// Kembalikan true jika item ditemukan dalam pohon

if ( simpul == null ) {

// Pohon kosong, jadi sudah pasti tidak ada item ini

// di dalamnya

return false;

}

else if ( item.equals(simpul.item) ) {

// Item ini ditemukan di simpul akar

return true;

}

else if ( item.compareTo(simpul.item) < 0 ) {

// Jika item lebih kecil dari simpul, maka mungkin ada di

// cabang kiri pohon. Kembalikan hasil pencarian di

// cabang kiri pohon

return pohonBerisi( simpul.kiri, item );

}

else {

// Jika item sama atau lebih besar dari simpul, maka

// mungkin ada di cabang kanan pohon. Kembalikan hasil

// pencarian di cabang kanan pohon

return pohonBerisi( simpul.kanan, item );

}

} // akhir pohonBerisi()

Ketika subrutin ini dipanggil, parameter pertama adalah variabel anggota statik akar yang

menunjuk pada akar seluruh pohonh pencarian biner.

Perlu dicatat bahwa rekursi bukan sesuatu yang penting dalam mengimplementasikan

subrutin ini. Algoritma pohon pencarian biner yang tidak rekursif mengikuti aturan berikut

: Turun ke bawah hingga kita menemukan item atau hingga mencapai null. Kita bisa

menggunakan perulangan while, sehingga implementasinya menjadi :

static boolean pohonBerisiNR( SimpulPohon akar, String item ) {

// Kembalikan true jika item ada di dalam pohon biner.

SimpulPohon pointer; // Pointer untuk menelusuri pohon

pointer = akar; // Mulai di akar simpul

while (true) {

if (pointer == null) {

// Kita sudah sampai pada akhir pohon, dan item belum

// ditemukan

return false;

}

else if ( item.equals(pointer.item) ) {

Page 262: Java With Eclipse

// Kita sudah menemukan item

return true;

}

else if ( item.compareTo(pointer.item) < 0 ) {

// Jika item lebih kecil, kemungkinan ada di cabang kiri

// Teruskan penelusuran di cabang kiri pohon

pointer = pointer.kiri;

}

else {

// Jika item lebih besar, kemungkinan ada di cabang kanan

// Teruskan penelusuran di cabang kanan pohon

pointer = pointer.kanan;

}

} // akhir while

} // akhir pohonBerisiNR();

Subrutin untuk menyisipkan item baru ke dalam pohon mirip dengan rutin pencari non-

rekursif di atas. Selain itu rutin penyisipan harus bisa menguji apakah pohon kosong atau

tidak. Jika pohon kosong, maka akar menunjuk pada simpul baru.

akar = new simpulPohon( itemBaru );

Akan tetapi, berarti akar tidak bisa diberikan sebagai parameter subrutin, karena kita tidak

bisa mengubah nilai yang disimpan dalam parameter aktual. (Ini bisa dilakukan dalam

bahasa pemrograman lain). Ada cari lainnya, akan tetapi cara lebih mudah adalah

menggunakan rutin penyisipan non-rekursif yang mengakses variabel anggota akar secara

langsung.

Perbedaan antara mencari dan menyisipkan item adalah kita harus berhati-hati untuk tidak

jatuh dari pohon. ARtinya, kita harus selesai melakukan pencarian sebelum pointer

bernilai null karena mencapai akhir pohon. Jika kita sampai pada tempat kosong, di situlah

kita menempatkan simpul baru kita.

static void sisipPohon(String itemBaru) {

// Tambahkan item ke dalam pohon pencarian biner, di mana

// variabel "akar" berisi. (Catatan kita tidak bisa menggunakan

// akar sebagai parameter, karena kita harus mengubah isi

// variabel ini.)

if ( akar == null ) {

// Jika pohon kosong, set akar ke simpul baru

// yang berisi itemBaru

akar = new SimpulPohon( itemBaru );

return;

Page 263: Java With Eclipse

}

SimpulPohon pointer; // Untuk menelusuri pohon

pointer = akar; // Mulai dari akar

while (true) {

if ( newItem.compareTo(pointer.item) < 0 ) {

// Karena itemBaru kurang dari item dalam pohon

// maka ia harus berada di cabang kiri pohon.

// Jika ada ruang kosong di pointer.kiri maka simpul baru

// bisa ditambah di sini. Jika tidak turun satu tingkat lagi

ke kiri

if ( pointer.kiri == null ) {

pointer.kiri = new SimpulPohon( itemBaru );

return; // itemBaru sudah ditambahkan ke dalam pohon

}

else

pointer = pointer.kiri;

}

else {

// Karena itemBaru lebih besar dari item dalam pohon

// maka ia harus berada di cabang kanan pohon.

// Jika ada ruang kosong di pointer.kanan maka simpul baru

// bisa ditambah di sini. Jika tidak turun satu tingkat lagi

ke kanan

if ( pointer.kanan == null ) {

pointer.kanan = new SimpulPohon( itemBaru );

return; // itemBaru sudah ditambahkan ke dalam pohon

}

else

pointer = pointer.kanan;

}

} // akhir while

} // akhir sisipPohon()

Bab IX - Pemrograman Generik dan Kelas Koleksi

Bagaimana caranya menghindari "reinventing the wheel", atau membuat kembali sesuatu

yang sudah ditemukan? Banyak struktur data dan algoritma, seperti yang sudah dibahas

pada bab sebelumnya telah dipelajari, diprogram, dan diprogram ulang oleh mahasiswa

ilmu komputer dalam beberapa generasi. Ini adalah kesempatan belajar yang sangat baik.

Page 264: Java With Eclipse

Sayangnya, algoritma dan data stuktur ini juga diprogram dan diprogram ulang oleh

profesional komputer. Mereka menghabiskan waktu yang tidak perlu untuk memrogram

ulang sesuatu yang sudah ada, bukannya membuat sesuatu yang lebih inovatif dan kreatif.

Programmer yang perlu menggunakan list atau pohon biner, seharusnya tidak perlu lagi

membuat data struktur ini dari awal. Struktur data ini sudah banyak dimengerti dan

diprogram ribuan kali sebelumnya. Masalahnya adalah bagaimana agar struktur data yang

tangguh tersedia dan siap digunakan oleh programmer. Di bagian ini kita akan melihat

bagaimana Java menyelesaikan masalah ini

Pemrograman generik adalah penulisan kode yang bisa digunakan oleh berbagai macam

tipe data. Kita telah menemukan istilahnya pada bagian sebelumnya tentang array dinamis

integer. Kode yang ditulis di sana untuk array dinamis integer hanya bisa bekerja untuk

tipe data int. Akan tetapi kode array dinamis untuk double, String, atau tipe data lainnya

hampir sama. Akan sangat tidak nyaman apabila kita harus mengkopi kodenya berulang-

ulang untuk tipe data yang berbeda-beda.

Seperti disebutkan sebelumnya, Java mencoba menyelesaikan masalah ini dengan

membuat kelas ArrayList. Kelas ini pada dasarnya merupakan array dinamis dengan tipe

Object. Karena semua kelas merupakan kelas turunan dari Object, maka objek yang

bertipe kelas apapun bisa disimpan dalam ArrayList.

Ini adalah contoh pemrograman generik : kode untuk kelas ArrayList cukup ditulis satu

kali, tetapi bisa digunakan untuk objek dengan tipe data yang berbeda-beda (akan tetapi,

tidak bisa digunakan untuk tipe data primitif, seperti int atau double.)

Kelas ArrayList hanyalah satu dari beberapa kelas dan interface yang merupakan

pemrograman generik pada Java. Kita akan lihat beberapa kelas lain dan bagaimana kelas-

kelas ini digunakan. Semua kelas yang didiskusikan pada bagian ini merupakan bagian dari

paket java.util dan kita perlu menambahkan pernyataan import di awal program untuk

bisa menggunakannya. (Sebelum kita menggunakan perintah import java.util.*; di

semua program, kita harus tahu bahwa beberapa kelas di dalam java.util memiliki nama

yang sama pada pake lain. Misalnya, java.util.List dan java.awt.List adalah kelas

yang bernama sama dengan paket yang berbeda).

Adalah sesuatu hal yang tidak mudah untuk mendesain pustaka untuk pemrograman

generik. Solusi yang disediakan Java memiliki banyak fitur bagus, akan tetapi bukan

berarti cara ini adalah cara satu-satunya. Sudah pasti bukan yang terbaik, akan tetapi dalam

konteks desain Java secara keseluruhan, mungkin lebih cocok disebut optimal. Untuk dapat

Page 265: Java With Eclipse

memberikan gambaran seperti apa pemrograman generik secara umum, mungkin akan

lebih baik untuk melihat sekilas pemrograman generik di bahasa pemrograman lain.

Pemrograman Generik pada Bahasa Pemrograman Lain

Pemrograman Generik pada Smalltalk

Smalltalk adalah salah satu bahasa pemrograman berorientasi objek pertama. Bahasa ini

masih digunakan hingga kini. Meskipun tidak menjadi sepopuler Java atau C++, bahasa ini

adalah sumber ide yang diadopsi banyak bahasa pemrograman. Pada Smalltalk, pada

dasarnya semua pemorgraman adalah generik, karena dua sifat bahasa ini.

Pertama, variabel pada Smalltalk tidak memiliki tipe. Suatu nilai memiliki tipe, seperti

integer atau string, tetapi variabel tidak memiliki nilai. Suatu variabel bisa menampung

jenis data apa saja. Parameter juga tidak memiliki tipe, sehingga subrutin bisa digunakan

pada parameter apa saja. Demikian juga dengan struktur data bisa menampung data apa

saja. Misalnya, sekali kita mendefinisikan struktur data pohon biner pada Smalltalk, kita

bisa menggunakannya untuk integer, string, tanggal, atau data apa saja. Kita tidak perlu

menulis kode baru untuk masing-masing tipe data.

Kedua, semua nilai adalah objek, dan semua operasi pada objek dilakukan dengan metode

dalam kelas. Hal ini juga berlaku bahkan untuk tipe data primitif seperti integer. Ketika

operator "+" digunakan untuk menjumlah integer, operasi ini dilakukan dengan memanggil

metode pada kelas integer. Ketika kita membuat kelas baru, kita bisa membuat operator

"+" sendiri kemudian kita bisa menjumlahkan dua objek dengan tipe kelas tersebut dengan

menggunakan "a + b" seperti kita menjumlahkan angka seperti biasa.

Sekarang misalnya kita membuat subrutin baru yang menggunakan operator "+" untuk

menjumlahkan masing-masing item di dalam list. Subtuin ini bisa digunakan untuk list

integer, tapi juga bisa digunakan ke tipe data apapun yang mendefinisikan "+". Demikian

juga dengan subrutin yang menggunakan operator "<" untuk mengurutkan list juga bisa

digunakan untuk list yang mengandung tipe data apapun yang memiliki definisi "<". Kita

tidak perlu menulis subrutin pengurutan untuk masing-masing tipe data.

Jika kedua fitur ini kita gabungkan, kita bisa memiliki bahasa di mana struktur data dan

algoritmanya akan bekerja untuk jenis tipe data apapun (yang masuk akal), yaitu jika

operasi yang sesuai telah didefinisikan. Inilah yang merupakan pemrograman generik yang

sesungguhnya.

Mungkin ini terdengar sangat baik, dan Anda mungkin bertanya-tanya kenapa tidak semua

bahasa pemrograman bekerja seperti ini. Kebebasan pemrograman seperti ini akan

Page 266: Java With Eclipse

memudahkan kita membuat program, akan tetapi akan lebih sulit untuk membuat program

yang benar dan tangguh.

Sekali kita membuat struktur data yang bisa menampung tipe data apa saja, akan sulit

untuk menjamin ia bisa menampung tipe data yang kita inginkan. Jika kita ingin suatu

subrutin mengurutkan tipe data apapun, maka akan sangat sulit untuk menjamin bahwa

subrutin ini hanya digunakan untuk data di mana operator "<" telah didefinisikan. Lebih

khusus, kompiler tidak bisa memastikannya. Masalah ini akan muncul di saat program

dijalankan ketika kita mencoba untuk menjalankan operasi tertentu pada suatu tipe data

yang belum ada, kemudian program akan crash.

Pemrograman Generik pada C++

Tidak seperti Smalltalk, C++ adalah bahasa pemrograman dengan tipe kuat, bahkan lebih

kuat dari Java. Setiap variabel memiliki tipe, dan hanya bisa menampung tipe data itu saja.

Artinya pemrograman generik seperti pada Smalltalk tidak mungkin diterapkan.

Lebih Jauh, C++ tidak memiliki sesuatu yang mirip dengan kelas Object pada Java.

Artinya, tidak ada kelas yang merupakan kelas super dari semua kelas. Artinya C++ tidak

bisa menggunakan pemrograman generik seperti Java.

Akan tetapi, C++ memiliki sistem pemrograman geneik yang canggih dan fleksibel, yaitu

yang disebut template. Dalam C++, kita tidak membuat subrutin pengurutan yang berbeda

untuk setiap tipe data. Akan tetapi kita bisa mmebuat template subrutin. Template ini

bukan subrutin; akan tetapi mirip seperti pabrik pembuat subrutin. Kita lihat contoh

berikutnya, karena sintaks C++ mirip dengan Java :

template<class TipeItem>

void urut( TipeItem A[], int banyak ) {

// Urut banyak item dalam array A, ke dalam urutan menaik

// Algoritma yang digunakan adalah pengurutan pilihan

for (int i = banyak-1; i > 0; i--) {

int posisi_maks = 0;

for (int j = 1; j <= banyak; j++)

if ( A[j] > A[posisi_maks] )

posisi_maks = j;

TipeItem temp = A[banyak];

A[banyak] = A[posisi_maks];

A[posisi_maks] = temp;

}

}

Page 267: Java With Eclipse

Dalam kode di atas, kita mendefinisikan template subrutin. Jika kita menghapus baris

pertama, yaitu template<class TipeItem>", dan mengganti kata "TipeItem" dengan

"int", pada semua isi template, maka kita bisa mendapatkan subrutin untuk mengurut array

int. (Meskipun kita tulis seperti "class TipeItem", kita bisa menggantinya dengan tipe

apapun, termasuk tipe primitif). Jika kita mengganti "TipeItem" dengan string, maka kita

bisa mendapatkan subrutin untuk mengurut array string. Ini yang sebenarnya dilakukan

oleh compiler.

Jika program kita menyebut "urut(list,10)" di mana list adalah array int, maka

kompiler menggunakan template ini untuk membuat subrutin untuk mengurut array int.

Jika kita sebut "urut(kartu,10)" di mana kartu adalah array objek bertipe Kartu, maka

kompiler akan membuat subrutin untuk mengurutkan array Kartu.

Template di atas menggunakan operator ">" untuk membandingkan nilai. Jika operator ini

didefinisikan untuk nilai bertipe Kartu, maka kompiler akan menggunakan template

dengan sukses. Jika ">" tidak didefinisikan untuk kelas Kartu, maka kompiler akan gagal,

akan tetapi ini akan terjadi pada saat program dikompilasi bukan seperi Smalltalk di mana

program akan crash pada saat dijalankan.

C++ juga memiliki template untuk membuat kelas. Jika kita menulis template untuk pohon

biner, kita bisa menggunakan template itu untuk membuat kelas pohon biner int, pohon

biner string, pohon biner tanggal, dan seterusnya -- semua dari satu template. Versi paling

baru C++ memiliki template bawaan yang cukup komplit yang disebut dengan Pustaka

Template Standar (Standard Template Library atau STL). STL sendiri cukup kompleks,

dan bahkan beberapa orang mengatakan sangat amat kompleks. Akan tetapi ini juga fitur

paling menarik dari C++

Pemrograman Generik pada Java

Seperti pada C++, Java adalah bahasa bertipe kuat. Akan tetapi, pemrograman generik

pada Java lebih dekat dengan Smalltalk daripada C++. Seperti telah dijelaskan

sebelumnya, pemrograman generik pada Java berdasarkan pada kelas Object yang

merupakan kelas super dari semua kelas. Hingga tingkat tertentu, ini membuat Java mirip

dengan Smalltalk : Struktur data yang didesain untuk menampung Object bisa digunakan

untuk menyimpan data kelas apapun. Kita tidak perlu membuat template atau fitur pada

bahasa pemrograman lain untuk mendukung pemrograman generik.

Tentunya, tipe primitif, seperti integer, bukan objek pada Java, dan karenanya tidak bisa

disimpan dalam tipe data generik. Dan sebenarnya, tidak ada cara untuk melakukan

Page 268: Java With Eclipse

pemrograman generik dengan tipe data primitif pada Java. Pendekatan Smalltalk tidak bisa

diterapkan pada Java kecuali untuk objek, dan pendekatan C++ tidak tersedia pada Java.

Lebih jauh, subrutin generik lebih bermasalah pada Java daripada Smalltalk atau C++.

Pada Smalltak, subrutin dapat dipanggil dengan parameter bertipe apapun, dan akan

bekerja asalkan operator yang digunakan pada subrutin didefinisikan pada parameternya.

Pada Java, parameter suatu subrutin harus bertipe tertentu. Dan subrutin hanya bisa

menggunakan operasi untuk tipe itu saja. Subrutin dengan parameter Object bisa

digunakan untuk objek tipe apa saja, akan tetapi subrutin hanya bisa menggunakan operasi

pada kelas Object saja, dan sebenarnya hanya sedikit operasi pada kelas Object!

Misalnya tidak ada operasi pembanding pada kelas Object, jadi kita tidak bisa membuat

algoritma pengurutan generik. Kita akan lihat bagaimana Java menyelesaikan masalah ini.

Karena masalah seperti ini, beberapa orang menyatakan bahwa Java tidak mendukung

pemrograman generik secara keseluruhan. Beberapa orang lain tidak setuju. Akan tetapi,

tetap saja ini tidak menghambat Java untuk digunakan secara luas.

Koleksi dan Map

Struktur data generik pada Java dapat dibagi menjadi dua kategori : koleksi dan map.

Koleksi kurang lebih mirip seperti kumpulan objek-objek. Map menghubungkan objek di

satu kumpulan dan objek di kumpulan lain seperti kamus yang menghubungkan definisi

dengan kata atau buku telepon menghubungkan nama dan nomor telepon. Map mirip

dengan apa yang kita sebut "list asosiasi" pada bagian sebelumnya.

Ada dua jenis koleksi : list dan himpunan (set). List adalah kumpulan objek di mana item-

itemnya diatur secara berurutan. List memiliki item pertama, item kedua, dan seterusnya.

Untuk item di dalam list, kecuali item terakhir, akan ada item yang ada di belakangnya.

Himpunan (set) adalah kumpulan objek di mana hanya ada satu objek yang bisa ada di

dalam suatu himpunan.

Lihat bahwa istilah "koleksi", "list", "himpunan", dan "map" tidak menyatakan bagaimana

data disimpan. List bisa diimplementasikan dalam bentuk array, list berantai, atau map

yang menghubungkan elemen list dengan angka 0, 1, 2, ....

Sebetulnya istilah-istilah ini dibuat pada Java bukan dalam bentuk kelas tapi dalam bentuk

interface. Interface Collection, List, Set dan Map mendefinisikan operasi dasar dari

struktur data tersebut, tapi tidak menjelaskan bagaimana struktur data dan operasinya

diimplementasikan.

Struktur data dan operasinya akan ditulis pada kelas yang mengimplementasikan interface

tersebut. Bahkan ketika kita menggunakan kelas tersebut, kita mungkin tidak tahu

Page 269: Java With Eclipse

bagaimana kelas tersebut diimplementasikan, kecuali kita melihat langsung pada kode

sumbernya. Struktur data generik pada Java adalah tipe data abstrak (abstract data type).

Mereka memiliki definisi operasi yang bisa dilakukan, dan bukan bagaimana data diatur

dalam memori komputer.

Kita akan lihat kelas list, set dan map pada bagian berikut. Tapi sebelum kita sampai pada

bagian itu, kita akan melihat dahulu beberapa konsep tentang operasi umum yang tersedia

pada semua koleksi.

Algoritma Generik

Interface Collection memiliki metode untuk melakukan beberapa operasi dasar pada

koleksi. Karena "koleksi" adalah konsep yang sangat umum, maka operasi yang bisa

dilakukan oleh semua koleksi juga sesuatu yang sangat umum. Misalnyakol adalah objek

yang mengimplementasi interface Collection. Berikut ini adalah operasi yang bisa

dilakukan.

kol.size() -- mengembalikan int yang berisi banyaknya objek dalam suatu koleksi

kol.isEmpty() -- mengembalikan boolean true jika koleksi kosong, atau ukurannya

sama dengan 0

kol.clear() -- menghapus semua objek dalam koleksi

kol.contains(objek) -- mengembalikan nilai boolean jika objek ada dalam koleksi

kol.add(objek) -- menambah objek ke dalam koleksi. Parameternya bisa berupa

Object apa saja. Beberapa koleksi bisa berisi nilai null, sementara yang lain tidak.

Misalnya menambahkan objek ke dalam Set tidak berpengaruh apa-apa jika objek

tersebut sudah ada di dalamnya.

kol.remove(objek) -- membuang objek dari dalam koleksi, jika ada, dan

mengembalikan nilai boolean yang menyatakan apakah objek tersebut ada atau

tidak di dalam koleksi

kol.containsAll(kol2) -- mengembalikan nilai boolean jika semua objek dalam kol2

ada di dalam koleksi kol. Parameternya bisa berupa Collection apa saja.

kol.addAll(col2) -- menambahkan semua objek yang ada dalam koleksi kol2 ke

dalam kol

kol.removeAll(kol2) -- menghapus semua objek di kol yang ada pada kol2

kol.retainAll(kol2) -- menghapus semua objek pada kol yang tidak ada pada kol2.

Hanya akan mempertahankan objek yang ada pada kol2

Page 270: Java With Eclipse

kol.toArray() -- mengembalikan array bertipe Object[] yang isinya semua item di

dalam koleksi. Nilai yang dikembalikan bisa di-tipe cast ke dalam tipe array lain,

jika perlu. Misalnya, jika kita tahu bahwa semua item di dalam kol bertipe String,

maka (String[])kol.toArray() akan mengembalikan array String yang berisi

semua string di dalam koleksi.

Karena semua metode adalah turunan dari interface Collection, maka metode ini harus

didefinisikan pada semua objek yang mengimplementasikan interface ini. Akan tetapi ada

masalah yang timbul. Misalnya ukuran suatu Collection tidak bisa diganti setelah dibuat.

Metode yang menambah atau membuang objek tidak bisa digunakan untuk koleksi jenis

ini. Meskipun mungkin legal untuk memanggil metode ini, pengecualian akan dilemparkan

ketika program dijalankan. Jenis pengecualiannya bernama

UnsupportedOperationException.

Ada juga masalah efisiensi. Meskipun suatu operasi didefinisikan untuk beberapa jenis

koleksi, tentunya unjuk kerjanya tidak sama untuk semua kelas. Bahkan untuk metode

sesederhana size() bisa sangat berbeda dari satu jenis koleksi ke jenis koleksi lainnya.

Untuk beberapa jenis koleksi, waktu pengolahan sebanding dengan jumlah item di dalam

koleksi. Koleksi lain mungkin memiliki variabel instansi yang melacak jumlah item di

dalam koleksi, sehingga menjalankan size() sama dengan mengambil nilai variabel

instansi ini. Artinya operasinya hanya dilakukan satu langkah saja, tidak peduli berapa

banyak item yang ada di dalam koleksi.

Ketika kita menggunakan koleksi, alangkah lebih baiknya untuk mengetahui seberapa

efisien suatu operasi dilakukan untuk jenis koleksi tertentu. Kita harus bisa memilih

koleksi yang cocok untuk masalah yang kita hadapi. Kita akan lihat lebih detail di bagian

berikutnya.

Iterator

Interface Collection mendefinisikan beberapa algoritma generik, akan tetapi mungkin

kita ingin membuat algoritma generik kita sendiri. Misalnya kita ingin membuat suatu

subrutin yang mencetak setiap item di dalam koleksi. Untuk bisa melakukan ini secara

generik, kita harus mencari akal bagaimana caranya untuk mengakses setiap item satu per

satu.

Kita telah lihat sebelumnya bagaimana caranya untuk data struktur tertentu : Misalnya

untuk array, kita bisa menggunakan perulangan for dari item pertama hingga terakhir.

Untuk list berantai, kita bisa menggunakan perulangan while dari item pertama hingga

Page 271: Java With Eclipse

kita menemukan null. Untuk pohon biner kita bisa menggunakan subrutin rekursif dengan

penelusuran infix.

Koleksi bisa dibuat dalam berbagai bentuk. Dengan keragaman bentuk ini dan bermacam-

macam cara untuk menelusurinya, bagaimana kita bisa membuat metode generik yang

berlaku untuk semua kenis koleksi? Masalah ini bisa diselesaikan dengan iterator.

Iterator adalah objek yang digunakan untuk menelusuri koleksi. Koleksi dengan jenis yang

berbeda memiliki jenis iterator yang berbeda pula, akan tetapi semua iterator digunakan

dengan cara yang sama. Algoritma yang menggunakan iterator untuk menelusuri koleksi

bisa dibuat generik, karena teknik yang sama bisa digunakan untuk beragam jenis koleksi.

Konsep iterator mungkin agak aneh untuk seseorang yang baru menemui konsep

pemrgoraman generik, akan tetapi Anda harus mengerti bahwa konsep ini bisa digunakan

untuk menyelesaikan masalah sulit dengan cara yang anggun.

Interface Collection memiliki metode yang bisa digunakan untuk mengambil iterator

dalam koleksi apapun. Jika kol adalah suatu koleksi, maka kol.iterator()

mengembalikan iterator yang bisa digunakan untuk menelusuri koleksi tersebut.

Bayangkan iterator seperti pointer generik yang dimulai dari awal koleksi dan bisa

berpindah dari satu item ke item lain. Iterator didefinisikan oleh interface yang bernama

Iterator. Interface ini hanya memiliki 3 metode. Jika iter merupakan variabel bertipe

Iterator, maka :

iter.next() -- mengembalikan item berikutnya, dan memindahkan iterator ke

item berikutnya. Nilai keluarannya bertipe Object. Ingat bahwa kita tidak bisa

melihat suatu item tanpa memindahkan iterator ke item berikutnya. Jika metode ini

dipanggil apabila tidak ada item lagi yang tersisa, ia akan melempar

NoSuchElementException.

iter.hasNext() -- mengembalikan nilai boolean yang memberi tahu apakah ada

item berikutnya yang bisa diproses. Kita harus mengetesnya sebelum memanggil

iter.next()

iter.remove() -- jika kita gunakan perintah ini setelah memanggil iter.next(),

maka ia akan menghapus item yang baru saja kita lihat. Akan melempar

pengecualian UnsupportedOperationException jika koleksi tidak bisa

menghapus item.

Dengan menggunakan iterator, kita bisa menulis kode untuk mencetak semua item dalam

koleksi apapun. Misalnya kol bertipe Collection. Maka kita bisa menggunakan :

Iterator iter = kol.iterator();

Page 272: Java With Eclipse

while ( iter.hasNext() ) {

Object item = iter.next();

System.out.println(item);

}

Bentuk yang sama bisa digunakan untuk pemrosesan jenis apapun. Misalnya, subrutin

berikut ini akan menghapus semua nilai null dari koleksi apapun (selama koleksi itu

mengizinkan penghapusan elemen) :

void removeNullValues( Collection coll ) {

Iterator iter = kol.iterator():

while ( iter.hasNext() ) {

Object item = iter.next();

if (item == null)

iter.remove();

}

}

Koleksi bisa menampung objek tipe apa saja, sehingga nilai keluaran iter.next() bertipe

Object. Sekarang, tidak ada yang bisa kita lakukan dengan variabel bertipe Object.

Dalam situasi praktis, koleksi digunakan untuk menyimpan objek yang bertipe kelas

tertentu, kemudian objek dari koleksi itu di-tipe cast ke kelas tersebut sebelum digunakan.

Misalnya, kita menggunakan kelas BentukGeometri. Misalnya kelas ini memiliki metode

gambar() untuk menggambar suatu gambar. Maka kita bisa membuat metode generik

untuk menggambar semua gambar dalam koleksi BentukGeometri seperti :

void gambarSemuaGambar( Collection koleksiGambar ) {

// Kondisi Awal: Semua item dalam koleksiGambar tidak berisi null

// dan bertipe kelas BentukGeometri

Iterator iter = koleksiGambar.iterator();

while ( iter.hasNext() ) {

BentukGeometri gbr = (BentukGeometri)iter.next();

gbr.gambar();

}

}

Kondisi awal dari metode tersebut menyatakan bahwa metode tersebut akan gagal apabila

item di dalam koleksi tidak bertipe BentukGeometri. Jika item ini ditemukan, maka tipe-

cast "(BentukGeometri)iter.next()" akan melempar pengecualian

ClassCastException.

Page 273: Java With Eclipse

Meskipun dalam Java kita tidak bisa memiliki "Koleksi Bentuk Geometri", dan hanya bisa

memiliki "Koleksi Object", dalam prakteknya ini bukan masalah besar. Jika hanya perlu

ingat objek jenis apa yang kita simpan dalam koleksi kita.

Kesamaan dan Perbandingan

Diskusi kita tentang metode pada interface Collection sebelumnya mengasumsikan

bahwa dua objek dianggap "sama". Misalnya metode kol.contains(objek) dan

kol.remove(objek) mencari item dalam koleksi yang sama dengan objek. Akan tetapi,

kesamaan bukan sesuatu yang mudah.

Teknik yang paling umum untuk menguji kesamaan (yaitu menggunakan operator ==)

sering kali tidak bisa digunakan untuk objek. Operator == yang digunakan pada objek

hanya membandingkan apakah objek tersebut memiliki alamat memori yang sama.

Biasanya, kita ingin supaya kesamaan berarti isi objeknya sama (bukan alamat

memorinya).

Dua nilai String dianggap sama jika keduanya memiliki urutan karakter yang sama.

Persoalan di mana karakter tersebut disimpan di dalam memori tentunya tidak relevan. Dua

nilai bertipe Date dianggap sama jika isinya adalah waktu yang sama.

Kelas Object memiliki metode bernilai boolean yaitu equals(Object) untuk menguji

apakah satu objek berisi sama dengan objek lain.

Di dalam koleksi, dua objek obj1 dan obj2 dianggap sama jika keduanya bernilai null

atau jika keduanya bernilai tidak nul dan obj1.equals(obj2) bernilai true.

Dalam kelas Object, obj1.equals(obj2) didefinisikan sebagai obj1 == obj2. Akan

tetapi pada hampir semua kelas turunan Object, definisi ini tidak masuk akal, dan

biasanya dibebanlebihkan (overload).

Kelas String misalnya, membebanlebihkan equals() sehingga untuk str.equals(obj)

bernilai sama jika urutan karakter obj sama dengan urutan karakter str.

Jika kita menulis kelas sendiri, kita mungkin ingin mendefinisikan metode equals()

sendiri pada kelas tersebut, sehingga metode kesamaan bisa berjalan sesuai dengan yang

kita inginkan. Misalnya kelas Kartu akan bekerja dengan benar jika digunakan dalam

koleksi bisa dibuat seperti :

public class Kartu { // Kelas untuk kartu mainan

int lambang; // Nomor 0 hingga 3 untuk lambangnya :

// wajik, diamon, keriting atau hati.

int angka; // Angka 1 hingga 13

Page 274: Java With Eclipse

public boolean equals(Object obj) {

if (obj == null || ! (obj instanceof Kartu) ) {

// obj tidak sama dengan Kartu ini jika obj

// tidak bertipe Kartu, atau isinya null

return false;

}

else {

Kartu lain = (Kartu)obj; // Type-cast obj ke Kartu

if (lambang == lain.lambang && angka == lain.angka) {

// Kartu lain berlambang dan berangka sama dengan

// kartu ini, artinya kedua kartu sama

return true;

}

else

return false;

}

}

... // metode dan konstruktor lain

}

Tanpa metode equals() dalam kelas ini, metode contains() dan remove() dalam

interface Collection tidak akan bekerja dengan baik untuk kelas Kartu.

Masalah yang sama akan muncul ketika item dalam koleksi akan diurutkan. Pengurutan

artinya mengatur urutan item dalam aturan tertentu. Masalahnya, tidak ada aturan "menaik"

dalam objek. Sebelum objek bisa diurutkan, metode tertentu harus dibuat untuk

membandingkan objek. Objek yang akan dibandingkan harus mengimplementasikan

interface java.lang.Comparable. Interface ini mendefinisikan satu metode, yaitu :

public int compareTo(Object obj)

Nilai yang dikembalikan obj1.compareTo(obj2) bernilai nol jika kedua objek berisi sama

(atau jika obj1.equals(obj2) bernilai benar). Keluarannya bernilai negatif jika obj1

lebih kecil dari obj2, atau jika diurutkan dalam urutan menaik obj1 akan muncul sebelum

obj2. Keluarannya bernilai positif jika obj1 lebih besar dari obj2 atau jika diurutkan

dalam urutan menaik obj1 muncul setelah obj2.

Kelas String mengimplementasikan interface Comparable dan memiliki metode

compareTo dengan cara seperti di atas. Jika kita ingin membuat kelas sendiri dan ingin

mengurut objek pada kelas itu, kita juga harus mengimplementasikan objek itu dengan cara

yang sama. Misalnya :

class NamaLengkap implements Comparable {

Page 275: Java With Eclipse

// Melambangkan nama lengkap yang terdiri dari

// nama depan dan nama belakang

String namaDepan, namaBelakang;

public boolean equals(Object obj) {

if (obj == null || ! (obj instanceof NamaLengkap)) {

return false;

}

else {

NamaLengkap lain = (NamaLengkap)obj;

return namaDepan.equals(lain.namaDepan)

&& namaBelakang.equals(lain.namaBelakang);

}

}

public void compareTo(Object obj) {

NamaLengkap lain = (NamaLengkap)obj;

// Menyebabkan error jika obj tidak bertipe NamaLengkap

if ( namaBelakang.compareTo(lain.namaBelakang) < 0 ) {

// Jika namaBelakang lebih kecil dari namaBelakang objek lain

// maka NamaLengkap ini muncul sebelum yang lain

// Kembalikan nilai negatif

return -1;

}

if ( namaBelakang.compareTo(lain.namaBelakang) > 0 ) {

// Jika namaBelakang lebih besar dari namaBelakang objek lain

// maka NamaLengkap ini muncul setelah yang lain

// Kembalikan nilai positif

return 1;

}

else {

// Nama belakangnya sama, sekarang bandingkan

// nama depannya

return namaDepan.compareTo(lain.namaDepan);

}

}

... // metode dan konstruktor lain

}

Page 276: Java With Eclipse

Ada cara lain untuk membandingkan objek pada Java, yaitu dengan membuat objek lain

untuk melakukan perbandingan. Objek tersebut harus mengimplementasikan

java.util.Comparator yang memiliki metode :

public int compare(Object obj1, Object obj2)

Metode ini membandingkan dua objek dan mengembalikan nilai negatif, nol, atau positif,

tergantung apakah obj1 muncul lebih dulu, sama, atau setelah obj2. Komparator ini

berfungsi untuk membandingkan objek yang tidak memiliki interface Comparable atau

untuk melakukan metode pembanding yang berbeda untuk objek yang bertipe sama.

Kita akan lihat bagaimana Comparable dan Comparator digunakan dalam koleksi dan map

Kelas Pembungkus

Seperti disebutkan sebelumnya, pemrograman generik pada Java tidak berlaku untuk tipe

primitif. Kita akan bahas masalah ini sebelum kita beranjak pada bagian berikutnya.

Kita tidak bisa menyimpan integer pada struktur data generik yang didesain untuk

menyimpan Object. Di lain pihak, tidak ada yang menghentikan kita untuk membuat

objek yang hanya berisi integer dan menggunakan objek itu untuk ditempatkan pada

struktur data.

Dalam kasus sederhana, kita akan membuat kelas yang tidak berisi apa-apa kecuali sebuah

integer :

public class KontainerInt {

public int nilai;

}

Sebenarnya, Java telah memiliki kelas seperti ini. Objek yang bertipe java.lang.Integer

berisi sebuah int saja. Kelas ini disebut kelas pembungkus (wrapper) untuk int. Nilai int

bisa dimasukkan dalam konstruktor, misalnya

Integer bil = new Integer(17);

membuat objek Integer yang membungkus angka 17. Objek Integer bisa digunakan

untuk struktur data generik dan dalam situasi lain jika objek dibutuhkan. Nilai int tersebut

disimpan dalam variabel instansi private final dari objek Integer.

Jika bil variabel yang menunjuk pada objek bertipe Integer, maka kita akan tahu berapa

bilangan yang disimpan dalam objek ini dengan memanggil metode instansi

bil.intValue(). Kita tidak bisa mengganti angka tersebut. Kita sebut objek Integer

sebagai objek <span style="color: #ff0000;">immutable</span> (objek yang tak

mungkin berubah, yaitu lawan kata dari mutable atau dapat bermutasi). Artinya, setelah

Page 277: Java With Eclipse

dibuat, kita tidak bisa mengubahnya. (Sama seperti objek bertipe String adalah objek

immutable).

Ada kelas pembungkus untuk semua tipe primitif. Semua objek dari tipe ini adalah objek

immutable. Kelas pembungkus untuk tipe double adalah java.lang.Double. Nilai di

dalam objek bertipe Double bisa diambil dengan metode instansi doubleValue().

Kelas pembungkus memiliki beberapa metode yang berguna. Beberapa digunakan untuk

mendukung pemrograman generik. Misalnya, kelas pembungkus mendefinisikan metode

equals(Object) dan compareTo(Object) dengan benar.

Metode lain dalam kelas pembungkus adalah fungsi bantu untuk bekerja dengan tipe

primitif. Misalnya, kita temukan sebelumnya metode Integer.parseInt(String) dan

Double.parseDouble(String). Fungsi ini digunakan untuk mengubah string seperti "42"

atau "2.3421213" ke dalam angka yang sesuai.

List

Ada dua cara paling umum untuk membuat list : sebagai array dinamis dan sebagai list

berantai. Kita telah membahas keduanya sebelumnya (di sini dan di sini). Keduanya

tersedia dalam bentuk generik dalam kelas java.util.ArrayList dan

java.util.LinkedList.

ArrayList adalah urutan objek yang disimpan dalam bentuk array yang ukurannya bisa

membesar jika item baru ditambahkan. Sedangkan LinkedList adalah urutan objek yang

disimpan dalam simpul yang terhubung dengan pointer seperti rantai. Kedua kelas ini

mengimplementasikan interface java.util.List, yang memiliki operasi yang sama untuk

semua jenis list.

Kedua list tersebut mendukung operasi list dasar. Tipe data abstrak didefinisikan dalam

bentuk operasi yang bisa dilakukannya, bukan bagaimana data disusun. Lalu kenapa kita

memiliki 2 kelas? Mengapa bukan satu kelas List saja dengan satu jenis struktur data?

Masalahnya, tidak ada satupun struktur data list yang bisa melakukan semua operasi list

secara efisien. Untuk operasi tertentu, list berantai lebih efisien daripada array. Untuk

operasi lainnya, array lebih efisien. Tergantung daripada bagaimana list tersebut

digunakan. Kita harus bisa memilih bentuk mana yang paling cocok dengan melihat

operasi apa yang sering digunakan.

Secara garis besar, kelas LinkedList lebih efisien untuk aplikasi di mana item-itemnya

lebih sering ditambah atau dibuang dari dalam list, terutama dari depan atau dari tengah

list. Dalam array, operasi tersebut memerlukan waktu yang cukup besar, karena untuk

Page 278: Java With Eclipse

setiap penyisipan atau pengurangan item, item harus digeser untuk membuat tempat

kosong baru ditengah list. Pada list berantai, simpul bisa diputus di tengah-tengah untuk

menambahkan item baru, yaitu dengan mengubah pointernya saja.

Kelas ArrayList lebih efisien digunakan jika akses random (random access) dibutuhkan

oleh aplikasi. Akses random maksudnya mengakses item ke-n di tengah-tengah list dan

pindah dari satu item ke item lain yang letaknya mungkin berjauhan.

Operasi yang bisa dilakukan dengan efisien oleh kedua kelas tersebut adalah mengurutkan

dan menambah item di akhir list.

Semua list mengimplementasikan metode dalam Collection, termasuk size(), isEmpty,

add(Object), remove(Object), dan clear().

Metode add(Object) digunakan untuk menambah objek di akhir list. Metode

remove(Object) dilakukan dengan mencari itemnya dahulu, yang tidak efisien dilakukan

pada list berantai karena pada list berantai item dibandingkan satu per satu dari awal list

hingga item ditemukan.

Interface List menambah beberapa metode untuk mengakses item pada list tergantung

dari urutannya dalam list. Untuk objek list bertipe List, metode-metode yang tersedia

adalah :

list.get(indeks) -- mengembalikan Object di posisi indeks dalam list, di mana

indeks dimulai dari 0, 1, 2, .... hingga list.size() - 1. Parameter indeks harus

ada dalam rentang ini, jika tidak maka pengecualian IndexOutOfBoundsException

akan dilemparkan.

list.set(indeks, obj) -- menyimpan obj di dalam list pada posisi indeks, dan

mengganti isi objek yang sudah ada di posisi tersebut. Metode ini tidak mengubah

jumlah elemen di dalam list atau memindahkan elemen lain.

list.add(indeks, obj) -- menyisipkan objek obj di dalam list pada posisi

indeks. Jumlah item di dalam list akan bertambah satu, dan item setelah posisi

indeks akan digeser ke belakang. Nilai indeks harus berada pada rentang 0 hingga

list.size().

list.remove(indeks) -- menghapus objek pada posisi indeks. Item setelah posisi

ini akan digeser maju untuk mengisi kekosongan objek pada posisi tersebut setelah

item dihapus.

list.indexOf(obj) -- mencari objek obj di dalam list dan mengembalikan

indeksnya pada list, jika ada. Jika objek yang dicari tidak ada maka nilai -1 akan

Page 279: Java With Eclipse

dikembalikan. Jika obj ada lebih dari satu dalam list, hanya indeks pertama yang

dikembalikan.

Metode-metode ini didefinisikan pada kelas ArrayList dan LinkedList, meskipun

metode-metode ini hanya efisien pada ArrayList. Kelas LinkedList memiliki beberapa

metode tambahan yang tidak ada pada ArrayList. Jika linkedlist adalah objek bertipe

LinkedList, maka

linkedlist.getFirst() -- mengembalikan Object pada posisi pertama di dalam

list. List tidak diubah sama sekali.

linkedlist.getLast() -- mengembalikan Object pada posisi terakhir di dalam

list. List tidak diubah sama sekali.

linkedlist.removeFirst() -- menghapus Objek pada posisi pertama di dalam

list. Object yang dihapus akan dikembalikan sebagai nilai keluaran.

linkedlist.removeLast() -- menghapus Objek pada posisi terakhir di dalam list.

Object yang dihapus akan dikembalikan sebagai nilai keluaran.

linkedlist.addFirst(obj) -- menambah obj pada posisi pertama di dalam list.

linkedlist.addLast(obj) -- menambah obj pada posisi terakhir di dalam list.

(Sama persis dengan linkedlist.add(obj))

Metode-metode di atas sepertinya dibuat sehingga LinkedList mudah digunakan sebagai

tumpukan atau antrian (lihat bagian sebelumnya dan bagian ini). Misalnya kita bisa

menggunakan LinkedList sebagai antrian dengan menambah item di akhir list

(menggunakan metode addLast()) dan menghapus item dari awal list (menggunakan

metode removeFirst()).

Jika list adalah objek bertipe List maka metode list.iterator() yang didefinisikan

pada interface Collection, akan mengembalikan Iterator yang bisa digunakan untuk

menelusuri list tersebut dari awal hingga akhir.

Akan tetapi, untuk List ada tipe Iterator khusus yang bernama ListIterator yang

memiliki kemampuan tambahan. Metode list.listIterator() mengembalikan objek

bertipe ListIterator dari list.

ListIterator memiliki metode Iterator biasa, seperti hasNext() dan next(), tetapi

juga hasPrevious() dan previous sehingga kita bisa melakukan penelusuran mundur.

Untuk mengerti lebih dalam, mungkin ada baiknya jika iterator diibaratkan sebagai iterator

yang menunjuk pada posisi di antara dua elemen, atau di akhir atau di depan list. Dalam

Page 280: Java With Eclipse

diagram berikut, item dalam list digambarkan sebagai kotak, dan panah menunjukkan

posisi dari iterator :

Jika iter adalah suatu ListIterator, maka

iter.next() memindahkan iterator satu tempat ke kanan dan mengembalikan item

yang baru dilewatinya.

Metode iter.previous() memindahkan iterator ke kiri dan mengambalikan item

yang dilewatinya.

Metode iter.remove() menghapus item dari dalam list. Item yang dihapus adalah

item yang baru saja dilewati oleh iter.next() atau iter.previous().

iter.add(Object) akan menambah Object ke dalam list pada posisi iterator

sekarang. Artinya bisa berada di tengah-tengah dua item, di awal list atau di akhir

list.

(Untuk pengetahuan, list yang digunakan oleh kelas LinkedList adalah list berantai ganda

(doubly linked list). Artinya, setiap simpul pada list memiliki dua pointer, yaitu satu yang

menunjuk pada simpul setelahnya dan satu lagi menunjuk pada simpul sebelumnya.

Sehingga metode next() dan previous() bisa diimplementasikan dengan efisien. Dan

juga, metode addLast() dan getLast() menjadi efisien karena kelas LinkedList juga

memiliki variabel instansi yang menunjuk pada simpul terakhir di dalam list.)

Sebagai contoh untuk menggunakan ListIterator, misalnya kita ingin menjaga agar

suatu list selalu terurut dalam urutan menaik. Ketika kita menambah item di dalam list, kita

bisa menggunakan ListIterator untuk menemukan posisi yang tepat di dalam list untuk

meletakkan item baru itu.

Idenya adalah memulai dari awal list, kemudian memindahkan iterator melalui semua item

yang lebih kecil dari item yang ingin sisipkan. Pada posisi tersebut, metode add() dari

iterator itu bisa digunakan untuk menyisipkan item pada posisi yang tepat.

Untuk menentukan bahwa item baru tersebut lebih besar dari item di dalam list, maka kita

asumsikan bahwa item dalam list sudah mengimplementasikan interface Comparable dan

memiliki metode compareTo(). (Interface ini didiskusikan pada bagian sebelumnya).

Berikut ini adalah metode untuk melakukannya.

static void sisipTerurut(List list, Comparable itemBaru) {

Page 281: Java With Eclipse

// Kondisi awal : Item dalam ist diurut dalam urutan menaik

// menurut metode compareTo.

// itemBaru.compareTo(item) harus didefinisikan untuk

// setiap item di dalam list.

//

// Kondisi akhir : itemBaru yang sudah ditambahkan

// ada pada posisi yang benar, sehingga list

// masih dalam keadaan terurut menaik

ListIterator iter = list.listIterator();

// Pindahkan iterator sehingga menunjuk pada posisi yang tepat

// Jika itemBaru lebih besar dari semua item di dalam list, maka

// perulangan while akan berhenti ketika iter.hasNext() bernilai

// false, yaitu ketika iterator sampai pada akhir list.

while (iter.hasNext()) {

Object item = iter.next();

if (itemBaru.compareTo(item) <= 0) {

// itemBaru harus ada SEBELUM item pada iter

// Pindahkan iterator satu tempat sehingga

// menunjuk pada posisi penyisipan yang benar,

// dan pada akhir perulangan.

iter.previous();

break;

}

}

iter.add(itemBaru);

} // akhir sisipTerurut()

Karena parameter pada metode ini bertipe List, maka metode ini bisa digunakan pada

ArrayList maupun LinkedList, dan kira-kira akan berjalan dengan tingkat efisiensi yang

sama untuk kedua jenis list. Mungkin akan lebih mudah untuk menulis metode di atas

dengan menggunakan indeks seperti pada array, dan dengan menggunakan metode

get(indeks) dan add(indeks,obj). Akan tetapi metode ini akan berjalan sangat lambat

untuk LinkedList karena get(indeks) pada LinkedList berjalan tidak efisien.

Page 282: Java With Eclipse

Pengurutan List

Mengurutkan list adalah operasi yang cukup umum dilakukan, dan seharusnya ada metode

untuk mengurut list di dalam interface List. Kenyataannya tidak ada.

Akan tetapi metode untuk mengurut List tersedia sebagai metode statik dalam kelas

java.util.Collections. Kelas ini memiliki berbagai metode statik yang digunakan

untuk membantu kelas-kelas koleksi :

Collections.sort(list) digunakan untuk mengurut list dalam urutan menaik.

Item di dalam list harus mengimplementasikan interface Comparable, misalnya

pada String.

Collections.sort(list,komparator), digunakan untuk mengurut list

menggunakan komparator (pembanding) sendiri yang diberikan di dalam metode.

Komparator adalah objek yang memiliki metode compare() yang digunakan untuk

membandingkan dua objek. Kita akan lihat contohnya nanti.

Collections.shuffle(list) digunakan untuk mengacak list.

Collections.reverse(list) digunakan untuk membalik urutan elemen di dalam

list, sehingga elemen terakhir menjadi elemen pertama dan sebaliknya.

Karena metode pengurutan yang cukup efisien sudah diberikan untuk List, kita tidak perlu

membuat metode pengurutan sendiri. Tentunya kita akan bertanya apakah ada metode

pengurutan untuk array biasa. Jawabannya adalah ya.

Pengurutan array juga tersedia dalam metode statik dalam kelas java.util.Arrays.

Arrays.sort(A) digunakan untuk mengurut array A, asalkan tipe dasar A adalah tipe

dasar primitif (kecuali boolean) atau jika A adalah array Object yang

mengimplementasikan interface Comparable.

Arrays.sort(A, indeksAwal, indeksAkhir) digunakan untuk mengurut sebagian

array saja. Ini penting karena array kadang hanya terisi sebagian. Perintah ini

mengurut array dari elemen A[indeksAwal], A[indeksAwal + 1], ... hingga

A[indeksAkhir - 1] dalam urutan menaik. Array.sort(A, 0, N) bisa

digunakan untuk mengurut N elemen pertama di array A.

Java tidak mendukung pemrograman generik untuk tipe primitif. Untuk

mengimplementasikan perintah Arrays.sort(A), kelas Arrays memiliki 8 metode : satu

metode untuk array Object dan masing-masing satu metode untuk tipe primitif byte,

short, int, long, float, double, dan char.

Page 283: Java With Eclipse

Set (Himpunan)

Himpunan (set) adalah kumpulan Object yang mana tidak boleh ada dua dari objek yang

sama di dalam satu himpunan. Objek obj1 dan obj2 adalah objek yang sama jika

obj1.equals(obj2) menghasilkan nilai true (lihat bagian sebelumnya untuk penjelasan

tentang ini).

Set mengimplementasikan metode umum pada Collection dengan sedikit modifikasi

sehingga tidak memiliki objek yang sama di dalamnya. Misalnya, jika set adalah objek

bertipe Set maka set.add(obj) tidak melakukan apa-apa jika obj sudah ada di dalam

set.

Java memiliki dua kelas yang mengimplementasikan interface Set, yaitu

java.util.TreeSet dan java.util.HashSet.

Selain sebagai Set, objek bertipe TreeSet memiliki sifat di mana elemen-elemennya

terurut dalam urutan menaik. Iterator dari suatu TreeSet akan mengunjungi elemen

dalam set tersebut dalam urutan menaik.

TreeSet tidak bisa menyimpan semua objek, karena objek yang disimpan harus bisa

diurutkan. Atau dengan kata iain, objek yang disimpan dalam TreeSet harus

mengimplementasikan interface Comparable dan obj1.compareTo(obj2) harus bisa

membandingkan 2 objek obj1 dan obj2 dengan baik. Atau cara lainnya adalah dengan

menggunakan Comparator yang bisa dimasukkan sebagai parameter dalam konstruktor

ketika TreeSet dibuat. Di sini Comparator digunakan untuk membandingkan objek yang

akan dimasukkan di dalam set.

Pada implementasi TreeSet, elemen di dalamnya disimpan dalam bentuk yang mirip

dengan pohon pengurutan biner (lihat bagian sebelumnya). Pohon yang digunakan bersifat

seimbang (balanced) yang artinya semua simpul daunnya berjarak sama dari akar

pohonnya. Dengan menggunakan pohon seimbang, kita bisa memastikan bahwa kita bisa

mencapai semua simpul daunnya dengan secepat mungkin. Menambah dan mengurangi

simpulnya juga bisa dilakukan dengan cepat.

Karena TreeSet mengurutkan elemen-elemennya dan menjamin tidak ada duplikasi objek,

TreeSet sangat berguna untuk beberapa aplikasi. Misalnya, kita akan membuat program

untuk menghitung semua kata dalam suatu artikel dan menampilkan semua kata yang

ditemukan. List kata-kata dalam artikel itu harus diurutkan, kemudian kata-kata yang sama

dihapus. Dengan menggunakan TreeSet, program tersebut dapat dibuat dengan sangat

mudah, menjadi :

Page 284: Java With Eclipse

TreeSet kata = new TreeSet();

while masih ada data dari input :

kt = kata berikutnya dari input

kata.add(kt);

Iterator iter = kata.iterator();

while (iter.hasNext()):

Tampikan iter.next() ke layar.

Contoh lainnya, misalnya kol adalah koleksi String (atau tipe lain yang

mengimplementasikan compareTo()). Kita bisa menggunakan TreeSet untuk

mengurutkan item dari kol dan menghapus duplikasi dengan :

TreeSet set = new TreeSet();

set.addAll(kol);

Perintah baris kedua di atas menambah semua elemen dari kol ke dalam set. Karena

berbentuk Set, maka duplikasi akan dihapus. Karena berbentuk TreeSet maka semua

elemennya akan terurut. Jika kita ingin data tersebut disimpan dalam struktur data lain, kita

bisa mengubahnya dengan mudah. Misalnya, untuk membuat ArrayList dari TreeSet,

bisa kita tulis dengan :

TreeSet set = new TreeSet();

set.addAll(kol);

ArrayList list = new ArrayList();

list.addAll(set);

Sebetulnya, kelas koleksi Java memiliki konstruktor yang bisa mengambil parameter

berbentuk Collection. Semua item dalam Collection ditambahkan ke dalam koleksi

baru ketika dibuat. Jadi new TreeSet(kol) membuat TreeSet yang memiliki elemen

yang sama seperti koleksi kol.

Atau kita bisa menyingkat keempat baris kode di atas dalam satu baris kode :

ArrayList list = new ArrayList( new TreeSet(kol) );

Hasilnya adalah list terurut dengan elemen dari kol tanpa duplikasi. Ini adalah contoh dari

keampuhan pemrograman generik.

(Sepertinya tidak ada struktur data list dengan duplikasi pada Java, atau TreeSet yang

membolehkan duplikasi. Pada bahasa pemrograman C++ struktur data ini disebut multiset.

Pada bahasa Smalltalk, struktur data seperti ini disebut bag (kantong). Pada Java, untuk

saat ini, tidak ada struktur data seperti ini.)

Page 285: Java With Eclipse

HashSet mengimplementasikan tabel hash yang akan kita bahas di bagian berikutnya.

Operasi penambahan, pencarian, dan penghapusan dilakukan dengan sangat efisien pada

tabel hash, bahkan lebih cepat lagi dari TreeSet. Elemen pada HashSet tidak disimpan

dalam urutan tertentu.

Iterator dari HashSet akan mengunjungi elemen-elemen pada HashSet dalam urutan

yang sepertinya acak dan mungkin berubah jika elemen baru ditambahkan.

Karena elemen pada HashSet tidak diurutkan, maka semua objek (termasuk yang tidak

mengimplementasikan interface Comparable) bisa dimasukkan dalam HashSet. Kita bisa

menggunakan HashSet jika elemen yang kita akan masukkan tidak bisa dibandingkan, atau

urutannya tidak penting, atau jika kecepatan lebih dipentingkan.

Kelas Map

Suatu array yang berisi N elemen bisa juga dilihat sebagai asosiasi (pemetaan) antara

elemennya dengan bilangan 0, 1, ..., N-1 yang merupakan indeksnya. Jika i adalah salah

satu bilangan ini, maka kita bisa mengambil elemen yang dipetakan oleh bilangan i, dan

juga kita bisa meletakkan elemen baru pada posisi ke-i.

Suatu peta (map) adalah generalisasi dari array. Seperti array, map juga memiliki operasi

untuk mengambil dan meletakkan elemen. Akan tetapi pada map, operasi ini tidak

dilakukan pada bilangan 0, 1, ... N-1, akan tetapi pada sembarang Object.

Beberapa bahasa pemrograman menggunakan istilah array asosiatif (associative array)

karena kesamaan perintah dengan array biasa. Pada bahasa pemrograman tersebut, kita

bisa menuliskan A["joko"] yang digunakan untuk memetakan "joko" pada suatu elemen

di dalam array.

Java tidak menggunakan perintah yang sama pada map, akan tetapi idenya serupa : Map

adalah seperti array yang indeksnya adalah objek sembarang, bukan integer. Pada map,

objek yang digunakan sebagai "indeks" disebut kunci (key). Objek yang ditunjuk oleh

indeks tersebut disebut nilai (value).

Satu kunci hanya boleh menunjuk pada satu nilai, akan tetapi satu nilai bisa ditunjuk oleh

beberapa kunci.

Dalam Java, map didefinisikan dalam interface java.util.Map, yang memiliki beberapa

metode untuk bekerja dengan map. Jika map adalah variabel dengan tipe Map, maka berikut

ini adalah beberapa metodenya :

map.get(kunci) -- mengembalikan Object yang ditunjuk oleh kunci. Jika map

tidak memiliki nilai yang ditunjuk oleh kunci, maka nilai null akan dikembalikan.

Page 286: Java With Eclipse

Tapi ingat juga bahwa mungkin saja kuncinya ada akan tetapi memang menunjuk

pada nilai null. Menggunakan "map.get(kunci)" sama dengan perintah

"A[kunci]" pada array A. (Akan tetapi pada map tidak ada pengecualian

IndexOutOfBoundsException)

map.put(kunci, nilai) -- Mengisi map dengan pasangan kunci dan nilai.

Kedua-dua kunci dan nilai bisa berupa objek apa saja. Jika map tersebut telah

memiliki kunci maka nilai yang ditunjuk akan diganti dengan yang baru diberikan.

Perintah ini mirip dengan "A[kunci] = nilai" pada array.

map.putAll(map2) -- jika map2 adalah map lain, maka perintah ini akan mengkopi

semua isi pada map2 ke dalam map.

map.remove(kunci) -- Jika map memiliki kunci yang menunjuk pada suatu nilai,

perintah ini akan menghapus kunci beserta nilai yang ditunjuknya, atau dengan

kata lain menghapus pasangan kunci dan nilai pada map sekaligus.

map.containsKey(kunci) -- mengembalikan nilai boolean true jika map memiliki

kunci yang merujuk pada suatu nilai

map.containsValue(nilai) -- mengembalikan nilai boolean true jika map

memiliki nilai yang ditunjuk oleh kunci apapun.

map.size() -- mengembalikan int yang berisi jumlah pasangan asosiasi pada

map.

map.isEmpty() -- mengembalikan boolean true jika map tidak berisi pasangan

asosiasi apa-apa.

map.clear() -- menghapus semua pasangan asosiasi dalam map.

Metode put dan get jelas merupakan metode yang paling sering digunakan dalam map.

Dalam banyak aplikasi, metode ini mungkin hanya metode ini yang kita butuhkan. Artinya,

menggunakan map sama mudahnya dengan menggunakan array biasa.

Java memiliki dua kelas yang mengimplementasikan interface Map, yaitu : TreeMap dan

HashMap.

Dalam TreeMap, pasangan kunci/nilai disimpan secara berurutan dalam pohon terurut,

yaitu diurut berdasarkan kuncinya. Supaya bisa bekerja dengan benar, maka hanya objek

yang bisa dibandingkan saja yang bisa digunakan sebagai kunci. Artinya kelas kunci harus

berupa kelas yang mengimplementasikan interface Comparable, atau Comparator harus

diberikan pada konstruktornya pada saat TreeMap dibuat.

Page 287: Java With Eclipse

HashMap tidak menyimpan pasangan kunci/nilai dalam urutan tertentu, sehingga tidak ada

batasan objek apa yang bisa disimpan di dalamnya. Hampir semua operasi dapat berjalan

lebih cepat pada HashMap dibandingkan dengan TreeMap.

Secara umum, lebih baik menggunakan HashMap kecuali kita butuh struktur data dalam

urutan tertentu yang hanya bisa dilakukan dengan TreeMap. Atau dengan kata lain, jika

kita hanya menggunakan perintah put dan get, gunakan HashMap.

Misalnya progrma direktori telefon, yaitu pada kelas BukuTelepon yang memiliki

pasangan nama/nomor telepon. Kelas ini memiliki operasi tambahEntri(nama, nomor)

dan ambilNomor(nama), di mana nama dan nomor bertipe String.

Dalam aplikasi pemrograman sebenarnya, kita tidak perlu lagi membuat kelas baru untuk

mengimplementasikan BukuTelepon tersebut, artinya kita bisa langsung menggunakan

Map. Akan tetapi menggunakan Map mungkin memiliki sedikit kerugian, karena kita

dipaksa harus menggunakan Object bukan String.

Jika ini masalahnya, maka kita bisa membuat kelas baru yang menggunakan Map dalam

implementasinya, seperti berikut :

import java.util.HashMap;

public class BukuTelepon {

// Menyimpan data telepon

private HashMap info = new HashMap();

public void tambahEntri(String nama, String nomor) {

// Menyimpan nomor telepon pada nama yang sesuai

info.put(nama,nomor);

}

public String ambilNomor(String nama) {

// Mengambil nomor telepon dari nama

// Kembalikan null jika tidak ada nomor telepon untuk nama tsb

return (String)info.get(nama);

}

} // akhir kelas BukuTelepon

Dalam metode ambilNomor di atas, nilai kembalian dari info.get(nama) di-type-cast ke

dalam String. Karena kembalian dari metode get() bertipe Object maka type cast

menjadi penting sebelum nilainya bisa digunakan.

Page 288: Java With Eclipse

Dengan "membungkus" Map di dalam kelas BukuTelepon, kita menyembunyikan type-cast

dalam implementasinya sehingga interaksi kelas ini dengan kelas lain yang

menggunakannya menjadi lebih natural.

Tampilan, subset, dan submap

Map sebenarnya bukan merupakan Collection, artinya map tidak mengimplementasikan

semua operasi yang didefinisikan pada interface Collection, karena map tidak memiliki

iterator. Akan tetapi, Java memiliki cara lain untuk melakukan penelusuran item pada map.

Jika map adalah variabel bertipe Map, maka metode

map.keySet()

akan mengembalikan semua objek kunci pada map. Karena di dalam map tidak ada kunci

yang sama, maka hasilnya akan dikembalikan dalam bentuk Set. Hasil yang dikembalikan

oleh map.keySet() disebut tampilan (view) dari objek-objek yang disimpan di dalam map.

"Tampilan" yang dikembalikan metode ini berhubungan langsung dengan map aslinya,

artinya jika kita menghapus elemen dari dalam tampilan ini, maka kunci beserta nilai yang

ditunjuk di dalam map akan juga dihapus. Tampilan tidak bisa digunakan untuk menambah

data, karena fungsinya hanya sebagai tampilan. Karena map.keySet() tidak membuat set

baru, maka perintah ini akan cukup efisien untuk digunakan pada map yang sangat besar

sekalipun.

Kita bisa menggunakan iterator untuk melakukan penelusuran isi Set. Kita bisa juga

menggunakan iterator untuk kunci dari map, kemudian menelusurinya, misalnya :

Set kunci = map.keySet(); // Set kunci dari map

Iterator kunciIter = kunci.iterator();

System.out.println("Map ini memiliki asosiasi sebagai berikut:");

while (kunciIter.hasNext()) {

Object knc = kunciIter.next(); // Ambil kunci berikut

Object nilai = map.get(knc); // Ambil nilai yang ditunjuk oleh kunci

System.out.println( " (" + knc + "," + nilai + ")" );

}

Jika map adalah suatu TreeMap, maka set kuncinya adalah set yang terurut, dan iteratornya

akan mengunjungi kunci-kuncinya dalam urutan menaik.

Interface Map memiliki 2 jenis tampilan lain. Jika map adalah variabel bertipe Map, maka

metode :

map.values()

mengembalikan Collection yang berisi semua nilai yang disimpan dalam map. Nilai

kembaliannya bukan Set karena suatu map bisa berisi nilai yang sama. Metode

Page 289: Java With Eclipse

map.entrySet()

mengembalikan Set yang berisi asosiasi (kunci dan nilainya) di dalam map. Informasi

dalam kelas ini sama dengan informasi dalam map itu sendiri, akan tetapi Set yang

dikembalikan menampilkan jenis informasi yang berbeda. Setiap elemen dalam set ini

adalah objek bertipe Map.Entry (Kelas ini adalah kelas statik bertingkat, sehingga

namanya memiliki tanda titik. Akan tetapi tetap bisa digunakan sebagaimana kelas-kelas

lainnya).

Objek Map.Entry berisi hanya satu pasangan kunci dan nilai, dan memiliki metode

getKey() dan getValue() untuk mengambil kunci dan nilainya. Ada juga metode

setValue(nilai) untuk mengisi nilainya. Kita bisa menggunakan set ini untuk mencetak

semua pasangan kunci dan nilainya, yang lebih efisien daripada menggunakan kunci

kemudian mengambil kembali isinya dari dalam map seperti contoh di atas.

Sehingga kita bisa membuat fungsi yang sama dengan di atas dengan lebih efisien, yaitu

Set entri = map.entrySet();

Iterator entriIter = entri.iterator();

System.out.println("Map ini berisi asosiasi sebagai berikut:");

while (entriIter.hasNext()) {

Map.Entry pasangan = (Map.Entry)entriIter.next();

Object kunci = pasangan .getKey(); // Ambil kunci dari pasangan

Object nilai = pasangan .getValue(); // Ambil nilainya

System.out.println( " (" + kunci + "," + nilai + ")" );

}

Map bukan satu-satunya tempat di mana tampilan bisa diambil. Misalnya, interface List

memiliki sublist yang untuk melihat sebagian isi list. Metode

List subList(int indeksAwal, int indeksAkhir)

mengembalikan sebagian list yang terdiri dari elemen pada posisi antara indeksAwal

hingga indeksAkhir (termasuk indeksAwal tapi tidak termasuk indeksAkhir.) Tampilan

ini memungkinkan kita untuk melakukan operasi apapun seperti pada list biasa, akan tetapi

sublist bukan list terpisah. Perubahan yang dilakukan pada sublist akan juga

mempengaruhi list aslinya.

Begitu juga dengan set, kita bisa membuat tampilan yang merupakan subset dari suatu set.

Jika set adalah suatu TreeSet, maka set.subSet(dariElemen, hinggaElemen)

mengembalikan [code]Set yang berisi elemen-elemen set antara dariElemen hingga

hinggaElemen (termasuk dariElemen tapi tidak termasuk hinggaElemen).

Page 290: Java With Eclipse

Misalnya, jika kata adalah suatu TreeSet di mana semua elemennya adalah String berisi

huruf kecil, maka kata.subSet("m", "n") akan berisi semua elemen pada kata yang

dimulai dari huruf m. Subset ini adalah tampilan yang merupakan bagian dari set aslinya.

Artinya membuat subset tidak mengkopi elemen, dan perubahan pada subset (misalnya

penambahan dan pengurangan elemen) juga mempengaruhi set aslinya.

Tampilan set.headSet(hinggaElemen) berisi semua elemen dari set yang

kurang dari [code]hinggaElemen, dan set.tailSet(dariElemen) adalah tampilan

yang berisi semua elemen yang lebih besar atau sama dengan dariElemen.

Kelas TreeMap memiliki tiga tampilan submap. Submap mirip dengan subset. Submat

adalah Map yang berisi subset dari kunci dan nilai pada Map aslinya. Jika map suatu variabel

bertipe TreeMap, maka map.subMap(dariKunci, hinggaKunci) mengembalikan

tampilan yang berisi semua pasangan kunci/nilai dari map yang kuncinya ada di antara

dariKunci dan hinggaKunci (termasuk dariKunci tapi tidak termasuk hinggaKunci).

Ada juga tampilan map.headMap(hinggaKunci) dan map.tailMap(dariKunci) seperti

aturan pada subset.

Misalnya, bukuHitam adalah suatu TreeMap di mana kuncinya adalah nama dan nilainya

adalah nomor telepon. Kita bisa mencetak semua entri pada bukuHitam yang namanya

dimulai dengan "M" seperti berikut :

Map ems = bukuHitam.subMap("M","N");

// Submap ini berisi semua elemen di mana kuncinya lebih

// besar dari "M" dan kurang dari (tapi tidak termasuk) "N"

if (ems.isEmpty())

System.out.println("Tidak ada elemen yang dimulai dengan M.");

else {

Iterator iter = ems.entrySet().iterator();

// Iterator ini akan menelusuri entri pada submap ems

while (iter.hasNext()) {

// Get the next entry and print its key and value.

Map.Entry entri = iter.next();

System.out.println( entri.getKey() + ": " + entri.getValue() );

}

}

Subset dan submap mungkin menjadi cara terbaik untuk melakukan pencarian, sehingga

kita bisa mencari elemen pada rentang nilai tertentu, bukan hanya pencarian satu nilai saja.

Misalnya pada database disimpan jadwal yang disimpan dalam bentuk TreeMap di mana

Page 291: Java With Eclipse

kuncinya adalah waktu, dan misalnya kita ingin mencari semua item yang terjadwal 1

Maret 2009. Kita hanya perlu membuat submap yang berisi semua kunci antara 00:00 1

Maret 2009 hingga 00:00 2 Maret 2009 kemudian mencetak isi submap tersebut. Tipe

pencarian seperti ini disebut pencarian rentang (subrange query).

Tabel Hash

HashSet dan HashMap diimplementasikan dengan struktur data yang disebut tabel hash.

Kita tidak perlu mengerti tabel hash untuk menggunakan HashSet atau HashMap, akan

tetapi programmer harus kenal dengan tabel hash dan cara kerjanya.

Tabel hash merupakan solusi elegan untuk menyelesaikan masalah pencarian. Tabel hash,

seperti HashMap, menyimpan pasangan kunci/nilai. Jika kita mengetahui kuncinya, maka

kita bisa mencari nilainya di dalam tabel. Jika tabel hash digunakan untuk

mengimplementasikan set, maka semua nilainya berisi null. Kita masih harus mencari

kuncinya di dalam tabel.

Dalam semua algoritma pencarian, untuk mencari suatu item, kita harus mencari item yang

tidak kita inginkan. Untuk mencari suatu item di dalam list tidak terurut, maka kita harus

mengecek isinya satu per satu hingga item yang kita cari ditemukan. Dalam pohon

pencarian biner, kita harus mulai dari akar dan turun ke bawah hingga item yang dicari

ditemukan.

Jika kita mencari pasangan kunci/nilai dalam tabel hash, kita bisa langsung ke tempat di

mana item tersebut berada. Kita tidak perlu mencari item lainnya (Sebenarnya tidak

sepenuhnya benar, tapi kira-kira seperti ini). Lokasi di mana pasangan kunci/nilai ini

berada dihitung dari kuncinya: Kita hanya melihat kuncinya, dan kita bisa pergi ke lokasi

di mana nilai tersebut disimpan secara langsung.

Bagaimana caranya? Jika kuncinya adalah integer antara 0 hingga 99, kita bisa menyimpan

pasangan kunci/nilai dalam array A yang berisi 100 elemen. Pasangan kunci/nilai dengan

kunci N bisa disimpan dalam A[N]. Kunci ini akan membawa kita langsung ke pasangan

kunci/nilai.

Masalahnya, ada banyak kemungkinan untuk suatu jenis kunci. Misalnya, jika kuncinya

bertipe int, maka kita perlu 4 milyar lokasi untuk menyimpan semua kuncinya dalam array

-- tentunya pemborosan memori yang sia-sia jika kita hanya ingin menyimpan katakan

ribuan item saja. Jika kuncinya berupa string dengan panjang yang tidak tentu, maka

jumlah kemungkinan kuncinya tidak terbatas, sehingga kita tidak mungkin menggunakan

array untuk menyimpan semua kemungkinan kuncinya.

Page 292: Java With Eclipse

Walau bagaimanapun, tabel hash menyimpan datanya dalam array. Indeks tidak sama

dengan kunci, akan tetapi indeks dihitung dari kunci. Indeks array dari hasil perhitungan

kunci disebut kode hash untuk kunci tersebut. Fungsi tertentu akan menghitung kode hash,

yang disebut fungsi hash. Untuk mencari kunci di dalam tabel hash, kita hanya pelu

menghitung kode hash kunci tersebut, kemudian langsung pergi ke lokasi array di ditunjuk

oleh kode hash tersebut. Jika kode hashnya 17, lihat langsung pada array nomor 17.

Sekarang, karena hanya ada lebih sedikit lokasi array dibandingkan kemungkinan

kuncinya, maka sangat mungkin kita memiliki situasi di mana satu lokasi array digunakan

dua atau lebih kunci. Hal ini disebut tabrakan (collision). Tabrakan bukan kesalahan. Kita

tidak bisa menolak suatu kunci karena memiliki kode hash yang sama dengan kunci lain.

Tabel hash harus bisa menangani tabrakan dengan cara yang baik.

Dalam tabel hash yang digunakan pada Java, setiap lokasi array sebetulnya adalah suatu

list berantai yang berisi pasangan kunci/nilai (atau mungkin juga list kosong). Jika dua

item memiliki kode hash yang sama, maka kedua item tersebut akan ada pada list yang

sama. Strukturnya bisa digambarkan sebagai berikut.

Pada gambar di atas, hanya ada satu item dengan kode hash 0, tidak ada item dengan kode

hash 1, dua item dengan kode hash 2, dan seterusnya. Pada tabel hash yang dirancang

dengan benar, hampir semua list berantai berisi nol atau satu elemen saja, dengan rata-rata

panjang list kurang dari 1. Meskipun kode hash dari suatu kunci mungkin tidak membawa

kita langsung pada kunci yang kita mau, akan tetapi tidak akan lebih dari satu atau dua

item yang harus kita cari sebelum kita sampai pada item yang kita inginkan.

Page 293: Java With Eclipse

Agar bekerja dengan benar, jumlah item dalam tabel hash harus kurang dari besarnya

array. Pada Java, katika jumlah item melebihi 75% ukuran array, maka array tersebut akan

diganti dengan array yang lebih besar dan semua item pada array yang lama dipindahkan

ke array baru.

Kelas Object memiliki metode bernama hashCode() yang mengembalikan nilai bertipe

int. Ketika objek obj disimpan dalam tabel yang berukuran N, maka kode hash antara 0

hingga N-1 diperlukan. Kode hash ini bisa dihitung dengan menggunakan

Math.abs(obj.hashCode()) % N, yaitu sisa pembagian dari nilai mutlak

obj.hashCode() dengan N. (Nilai mutlak diperlukan karena obj.hashCode() bisa

bernilai negatif, dan kita tidak ingin nilai negatif sebagai indeks array kita).

Supaya hash bisa bekerja dengan benar, dua objek yang sama menurut metode equals()

seharusnya memiliki kode hash yang sama. Dalam kelas Object metode equals() dan

hashCode() dihitung berdasarkan lokasi memori di mana objek tersebut disimpan. Akan

tetapi seperti disebutkan sebelumnya, banyak kelas yang memiliki metode equals()

sendiri.

Jika suatu kelas memiliki metode equals() sendiri, dan jika objek tersebut akan

digunakan sebagai kunci pada tabel hash, maka kelas tersebut juga harus mendefinisikan

metode hashCode().

Misalnya dalam kelas String, metode equals() didefinisi ulang sehingga dua objek

String dianggap sama jika urutan karakter dalam String tersebut sama. Metode

hashCOde() pada kelas String itu juga didefinisi ulang sehingga kode hash dari string

dihitung berdasarkan karakter di dalam String, bukan lokasi memorinya.

Untuk kelas standar Java, kita bisa berharap bahwa metode equals() dan hashCode sudah

dibuat dengan benar, akan tetapi untuk kelas yang kita buat sendiri kita mungkin harus

membuat fungsi hash sendiri.

Bab X - Pengenalan Input/Output (I/O)

Program komputer bisa berguna jika ia bisa berinteraksi dengan dunia lain. Interaksi di sini

maksudnya input/output atau I/O. Pada bab ini, kita akan melihat input output pada file dan

koneksi jaringan (network). Pada Java, input/output pada file dan jaringan dilakukan

berdasarkan aliran (stream), di mana semua objek dapat melakukan perintah I/O yang

sama. Standar output (System.out) dan standar input (System.in) adalah contoh aliran.

Untuk bekerja dengan file dan jaringan, kita membutuhkan pengetahuan tentang

pengecualian, yang telah dibahas sebelumnya. Banyak subrutin yang digunakan untuk

Page 294: Java With Eclipse

bekerja dengan I/O melemparkan pengecualian yang wajib ditangani. Artinya subrutin

tersebut harus dipanggil di dalam pernyataan try ... catch sehingga pengecualian yang

terjadi bisa ditangani dengan baik.

Stream, Reader, dan Writer

Tanpa bisa berinteraksi dengan dunia lain, suatu program tidak ada gunanya. Interaksi

suatu program dengan dunia lain sering disebut input/output atau I/O. Sejak dulu, salah

satu tantangan terbesar untuk mendesain bahasa pemrograman baru adalah mempersiapkan

fasilitas untuk melakukan input dan output. Komputer bisa terhubung dengan beragam

jenis input dan output dari berbagai perangkat. Jika bahasa pemrograman harus dibuat

secara khusus untuk setiap jenis perangkat, maka kompleksitasnya akan tak lagi bisa

ditangani.

Salah satu kemajuan terbesar dalam sejarah pemrograman adalah adanya konsep (atau

abstraksi) untuk memodelkan perangkat I/O. Dalam Java, abstraksi ini disebut dengan

aliran (stream). Bagian ini akan memperkenalkan tentang aliran, akan tetapi tidak

menjelaskan dengan komplit. Untuk lebih lengkapnya, silakan lihat dokumen resmi Java.

Ketika berhubungan dengan input/output, kita harus ingat bahwa ada dua kategori data

secara umum : data yang dibuat oleh mesin, dan data yang bisa dibaca manusia. Data yang

dibuat mesin ditulis dengan model yang sama dengan bagaimana data tersebut disimpan di

dalam komputer, yaitu rangkaian nol dan satu. Data yang bisa dibaca manusia adalah data

dalam bentuk rangkaian huruf. Ketika kita membaca suatu bilangan 3.13159, kita

membacanya sebagai rangkaian huruf yang kita terjemahkan sebagai angka. Angka ini

akan ditulis dalam komputer sebagai rangkaian bit yang kita tidak mengerti.

Untuk menghadapi kedua jenis data ini, Java memiliki dua kategori besar untuk aliran :

aliran byte untuk data mesin (byte stream), dan aliran karakter (character stream) untuk

data yang bisa dibaca manusia. Ada banyak kelas yang diturunkan dari kedua kategori ini.

Setiap objek yang mengeluarkan data ke aliran byte masuk sebagai kelas turunan dari

kelas abstrak OutputStream. Objek yang membaca data dari aliran byte diturunkan dari

kelas abstrak InputStream. Jika kita menulis angka ke suatu OutputStream, kita tidak

akan bisa membaca data tersebut karena ditulis dalam bahasa mesin. Akan tetapi data

tersebut bisa dibaca kembali oleh InputStream. Proses baca tulis data akan menjadi sangat

efisien, karena tidak ada penerjemahan yang harus dilakukan : bit yang digunakan untuk

menyimpan data di dalam memori komputer hanya dikopi dari dan ke aliran tersebut.

Untuk membaca dan menulis data karakter yang bisa dimengerti manusia, kelas utamanya

adalah Reader dan Writer. Semua kelas aliran karakter merupakan kelas turunan dari

Page 295: Java With Eclipse

salah satu dari kelas abstrak ini. Jika suatu angka akan ditulis dalam aliran Writer,

komputer harus bisa menerjemahkannya ke dalam rangkaian karakter yang bisa dibaca

maunsia.

Membaca angka dari aliran Reader menjadi variabel numerik juga harus diterjemahkan,

dari deretan karakter menjadi rangkaian bit yang dimengerti komputer. (Meskipun untuk

data yang terdiri dari karakter, seperti dari editor teks, masih akan ada beberapa terjemahan

yang dilakukan. Karakter disimpan dalam komputer dalam nilai Unicode 16-bit. Bagi

orang yang menggunakan alfabet biasa, data karakter biasanya disimpan dalam file dalam

kode ASCII, yang hanya menggunakan 8-bit. Kelas Reader dan Writer akan menangani

perubahan dari 16-bit ke 8-bit dan sebaliknya, dan juga menangani alfabet lain yang

digunakan negara lain.)

Adalah hal yang mudah untuk menentukan apakah kita harus menggunakan aliran byte

atau aliran karakter. Jika kita ingin data yang kita baca/tulis untuk bisa dibaca manusia,

maka kita gunakan aliran karakter. Jika tidak, gunakan aliran byte. System.in dan

System.out sebenarnya adalah aliran byte dan bukan aliran karakter, karenanya bisa

menangani input selain alfabet, misalnya tombol enter, tanda panah, escape, dsb.

Kelas aliran standar yang akan dibahas berikutnya didefinisikan dalam paket java.io

beserta beberapa kelas bantu lainnya. Kita harus mengimpor kelas-kelas tersebut dari paket

ini jika kita ingin menggunakannya dalam program kita. Artinya dengan menggunakan

"import java.io.*" di awal kode sumber kita.

Aliran tidak digunakan dalam GUI, karena GUI memiliki aliran I/O tersendiri. Akan tetapi

kelas-kelas ini digunakan juga untuk file atau komunikasi dalam jaringan. Atau bisa juga

digunakan untuk komunikasi antar thread yang sedang bekerja secara bersamaan. Dan juga

ada kelas aliran yang digunakan untuk membaca dan menulis data dari dan ke memori

komputer.

Operasi pada Aliran (Stream)

Kelas dasar I/O Reader, Writer, InputStream dan OutputStream hanya menyediakan

operasi I/O sangat dasar. Misalnya, kelas InputStream memiliki metode instansi

public int read() throws IOException

untuk membaca satu byte data dari aliran input. Jika sampai pada akhir dari aliran input ,

metode read() akan mengembalikan nilai -1. Jika ada kesalahan yang terjadi pada saat

pengambilan input, maka pengecualian IOException akan dilemparkan. Karena

IOException adalah kelas pengecualian yang harus ditangani, artinya kita harus

menggunakan metode read() di dalam penyataan try atau mengeset subrutin untuk

Page 296: Java With Eclipse

throws IOException. (Lihat kembali pembahasan tentang pengecualian di bab

sebelumnya)

Kelas InputStream juga memiliki metode untuk membaca beberapa byte data dalam satu

langkah ke dalam array byte. Akan tetapi InputStream tidak memiliki metode untuk

membaca jenis data lain, seperti int atau double dari aliran. Ini bukan masalah karena

dalam prakteknya kita tidak akan menggunakan objek bertipe InputStream secara

langsung. Yang akan kita gunakan adalah kelas turunan dari InputStream yang memiliki

beberapa metode input yang lebih beragam daripada InputStream itu sendiri.

Begitu juga dengan kelas OutputStream memiliki metode output primitif untuk menulis

satu byte data ke aliran output, yaitu metode

public void write(int b) throws IOException

Tapi, kita hampir pasti akan menggunakan kelas turunannya yang mampu menangani

operasi yang lebih kompleks.

Kelas Reader dan Writer memiliki operasi dasar yang hampir sama, yaitu read dan

write, akan tetapi kelas ini berorientasi karakter (karena digunakan untuk membaca dan

menulis data yang bisa dibaca manusia). Artinya operasi baca tulis akan mengambil dan

menulis nilai char bukan byte. Dalam prakteknya kita akan menggunakan kelas turunan

dari kelas-kelas dasar ini.

Salah satu hal menarik dari paket I/O pada Java adalah kemungkinan untuk menambah

kompleksitas suatu aliran dengan membungkus aliran tersebut dalam objek aliran lain.

Objek pembungkus ini juga berupa aliran, sehingga kita juga bisa melakukan baca tulis

dari objek yang sama dengan tambahan kemampuan dalam objek pembungkusnya.

Misalnya, PrintWriter adalah kelas turunan dari Writer yang memiliki metode

tambahan untuk menulis tipe data Java dalam karakter yang bisa dibaca manusial. Jika kita

memiliki objek bertipe Writer atau turunannya, dan kita ingin menggunakan metode pada

PrintWriter untuk menulis data, maka kita bisa membungkus objek Writer dalam objek

PrintWriter.

Contoh jika baskomKarakter bertipe Writer, maka kita bisa membuat

PrintWriter printableBaskomKarakter = new PrintWriter(baskomKarakter);

Ketika kita menulis data ke printableBaskomKarakter dengan menggunakan metode

pada PrintWriter yang lebih canggih, maka data tersebut akan ditempatkan di tempat

yang sama dengan apabila kita menulis langsung pada baskomKarakter. Artinya kita

hanya perlu membuat antar muka yang lebih baik untuk aliran output yang sama. Atau

Page 297: Java With Eclipse

dengan kata lain misalnya kita bisa menggunakan PrintWriter untuk menulis file atau

mengirim data pada jaringan.

Untuk lengkapnya, metode pada kelas PrintWriter memiliki metode sebagai berikut :

// Metode untuk menulis data dalam

// bentuk yang bisa dibaca manusia

public void print(String s)

public void print(char c)

public void print(int i)

public void print(long l)

public void print(float f)

public void print(double d)

public void print(boolean b)

// Menulis baris baru ke aliran

public void println()

// Metode ini sama dengan di atas

// akan tetapi keluarannya selalu

// ditambah dengan baris baru

public void println(String s)

public void println(char c)

public void println(int i)

public void println(long l)

public void println(float f)

public void println(double d

public void println(boolean b)

Catatan bahwa metode-metode di atas tidak pernah melempar pengecualian IOException.

Akan tetapi, kelas PrintWriter memiliki metode

public boolean checkError()

yang akan mengembalikan true jika ada kesalahan yang terjadi ketika menulis ke dalam

aliran. Kelas PrintWriter menangkap pengecualian IOException secara internal, dan

mengeset nilai tertentu di dalam kelas ini jika kesalahan telah terjadi. Sehingga kita bisa

menggunakan metode pada PrintWriter tanpa khawatir harus menangkap pengecualian

yang mungkin terjadi. Akan tetapi, jika kita ingin membuat progam yang tangguh tentunya

kita harus selalu memanggil checkError() untuk melihat apakah kesalahan telah terjadi

ketika kita menggunakan salah satu metode pada PrintWriter.

Page 298: Java With Eclipse

Ketika kita menggunakan metode PrintWriter untuk menulis data ke aliran, data tersebut

diubah menjadi rangkaian karakter yang bisa dibaca oleh manusia. Bagaimana caranya jika

kita ingin membuat data dalam bentuk bahasa mesin?

Paket java.io memiliki kelas aliran byte, yaitu DataOutputStream yang bisa digunakan

untuk menulis suatu data ke dalam aliran dalam format biner. DataOutputStream

berhubungan erat dengan OutputStream seperti hubungan antara PrintWriter dan

Writer.

Artinya, OutputStream hanya berisi metode dasar untuk menulis byte, sedangkan

DataOutputStream memiliki metode writeDouble(double x) untuk menulis nilai

double, writeInt(int x) untuk menulis nilai int, dan seterusnya. Dan juga kita bisa

membungkus objek bertipe OutputStream atau turunannya ke dalam aliran

DataOutputStream sehingga kita bisa menggunakan metode yang lebih kompleks.

Misalnya, jika baskomByte adalah variabel bertipe OutputStream, maka

DataOutputStream baskomData = new DataOutputStream(baskomByte);

untuk membungkus baskomByte dalam baskomData.

Untuk mengambil data dari aliran, java.io memiliki kelas DataInputStream. Kita bisa

membungkus objek bertipe InputStream atau turunannya ke dalam objek bertipe

DataInputStream. Metode di dalam DataInputStream untuk membaca data biner bisa

menggunakan readDouble(), readInt() dan seterusnya. Data yang ditulis oleh

DataOutputStream dijamin untuk bisa dibaca kembali oleh DataInputStream, meskipun

data kita tulis pada satu komputer dan data dibaca pada komputer jenis lain dengan sistem

operasi berbeda. Kompatibilitas data biner pada Java adalah salah satu keunggulan Java

untuk bisa dijalakan pada beragam platform.

Salah satu fakta yang menyedihkan tentang Java adalah ternyata Java tidak memiliki kelas

untuk membaca data dalam bentuk yang bisa dibaca oleh manusia. Dalam hal ini Java

tidak memiliki kelas kebalikan dari PrintWriter sebagaimana DataOutputStream dan

DataInputStream. Akan tetapi kita tetap bisa membuat kelas ini sendiri dan

menggunakannya dengan cara yang persis sama dengan kelas-kelas di atas.

Kelas PrintWriter, DataInputStream, dan DataOutputStream memungkinkan kita

untuk melakukan input dan output semua tipe data primitif pada Java. Pertanyaannya

bagaimana kita melakukan baca tulis suatu objek?

Page 299: Java With Eclipse

Mungkin secara tradisional kita akan membuat fungsi sendiri untuk memformat objek kita

menjadi bentuk tertentu, misalnya urutan tipe primitif dalam bentuk biner atau karakter

kemudian disimpan dalam file atau dikirim melalui jaringan. Proses ini disebut serialisasi

(serializing) objek.

Pada inputnya, kita harus bisa membaca data yang diserialisasi ini sesuai dengan format

yang digunakan pada saat objek ini diserialisasi. Untuk objek kecil, pekerjaan semacam ini

mungkin bukan masalah besar. Akan tetapi untuk ukuran objek yang besar, hal ini tidak

mudah.

Akan tetapi Java memiliki cara untuk melakukan input dan output isi objek secara

otomatis, yaitu dengan menggunakan ObjectInputStream dan ObjectOutputStream.

Kelas-kelas ini adalah kelas turunan dari InputStream dan OutputStream yang bisa

digunakan untuk membaca dan menulis objek yang sudah diserialisasi.

ObjectInputStream dan ObjectOutputStream adalah kelas yang bisa dibungkus oleh

kelas InputStream dan OutputStream lain. Artinya kita bisa melakukan input dan output

objek pada aliran byte apa saja.

Metde untuk objek I/O adalah readObject() yang tersedia pada ObjectInputStream dan

writeObject(Object obj) yang tersedia dalam ObjectOutputStream. Keduanya bisa

melemparkan IOException. Ingat bahwa readObject() mengembalikan nilai bertipe

Object yang artinya harus di-type cast ke tipe sesungguhnya.

ObjectInputStream dan ObjectOutputStream hanya bekerja untuk objek yang

mengimplementasikan interface yang bernama Serializable. Lbih jauh semua variabel

instansi pada objek harus bisa diserialisasi, karena interface Serializable tidak

mempunyai metode apa-apa. Interface ini ada hanya sebagai penanda untuk kompiler

supaya kompiler tahu bahwa objek ini digunakan untuk baca tulis ke suatu media.

Yang perlu kita lakukan adalah menambahkan "implements Serializable" pada definisi

kelas. Banyak kelas standar Java yang telah dideklarasikan untuk bisa diserialisasi,

termasuk semua komponen kelas Swing dan AWT. Artinya komponen GUI pun bisa

disimpan dan dibaca dari dalam perangkat I/O menggunakan ObjectInputStream dan

ObjectOutputStream.

Berbagai Jenis InputStream dan OutputStream

InputStream

Beberapa kelas turunan dari InputStream dapat dirangkum dalam tabel di bawah ini :

Kelas Kegunaan Argumen yang dibutuhkan

Page 300: Java With Eclipse

untuk membuat objek

ByteArrayInputStreamMenggunakan buffer pada

memori sebagai aliran input

Buffer yang akan digunakan

sebagai aliran input

StringBufferInputStreamMengubah string menjadi

InputStream

Suatu String (di dalamnya

sebenarnya menggunakan

StringBuffer)

FileInputStreamUntuk membaca informasi

dari dalam file

String yang berupa nama

suatu file, atau objek bertipe

File atau FileDescriptor

PipedInputStream

Menghasilkan data yang

ditulis oleh

PipedOutputStream.

Mengimplementasi konsep

"piping". Bisa digunakan

untuk multi-threading

Objek PipedOutputStream

SequenceInputStream

Menggabungkan dua atau

lebih InputStream menjadi

satu InputStream

Dua atau lebih objek bertipe

InputStream atau kontainer

bertipe Enumeration yang

berisi InputStream yang

akan digabungkan

FilterInputStream

Kelas abstrak yang merupakan

interface dari beberapa kelas

bantu untuk menggunakan

InputStream lain

FilterInputStream adalah lapisan di atas InputStream yang berguna untuk memberi

landasan pada kelas-kelas dekorator di atas. Kenapa dekorator? Karena kelas-kelas ini

hanya memberikan fungsionalitas tambahan, akan tetapi tidak mengubah bagaimana I/O

itu sendiri bekerja. Seperti disebutkan sebelumnya, bahwa kelas dasar InputStream dan

OutputStream hanya memiliki metode-metode paling sederhana. Kelas-kelas ini

memperbanyak metode baca/tulis untuk kemudahan pemrograman.

Kelas FilterInputStream sendiri terdiri dari beberapa jenis, yang bisa dirangkum dalam

tabel berikut ini :

Page 301: Java With Eclipse

Kelas Kegunaan

Argumen yang

dibutuhkan untuk

membuat objek

DataInputStream

Digunakan bersama-sama dengan

DataOutputStream sehingga kita bisa

menulis tipe data primitif, kemudian

membacanya kembali tanpa harus

diformat sendiri

InputStream

BufferedInputStream

Digunakan untuk menghindari

pembacaan langsung dari media secara

fisik setiap kali perintah read()

diberikan. Atau dengan kata lain

"gunakan buffer" untuk baca tulis

InputStream

dengan

kemungkinan

menentukan besar

buffer sendiri

LineNumberInputStream

Mencatat nomor baris dalam

InputStream. Kita bisa menggunakan

perintah getLineNumber() dan

setLineNumber(int)

InputStream

PushBackInputStream

Memiliki satu byte buffer sehingga kita

bisa meletakkan kembali karakter yang

sudah diambil (dibaca)

InputStream

OutputStream

Beberapa kelas turunan dari OutputStream dapat dirangkum dalam tabel di bawah ini :

Kelas Kegunaan

Argumen yang

dibutuhkan untuk

membuat objek

ByteArrayOutputStream

Membuat buffer dalam memori.

Semua data yang kita kirim akan

disimpan di memori ini.

Opsional untuk

memberikan besar buffer

yang akan disiapkan

FileOutputStreamUntuk menulis informasi ke dalam

file

String yang berupa nama

suatu file, atau objek

Page 302: Java With Eclipse

bertipe File atau

FileDescriptor

PipedOutputStream

Informasi yang kita kirim di aliran

output ini akan berakhir pada objek

bertipe PipedInputStream.

Mengimplementasi konsep "piping".

Bisa digunakan untuk multi-

threading

Objek

PipedInputStream

FilterOutputStream

Kelas abstrak yang merupakan

interface dari beberapa kelas bantu

untuk menggunakan OutputStream

lain.

Kelas FilterOutputStream sendiri terdiri dari beberapa jenis, yang bisa dirangkum dalam

tabel berikut ini :

Kelas Kegunaan

Argumen yang

dibutuhkan untuk

membuat objek

DataOutputStream

Digunakan bersama-sama dengan

DataInputStream sehingga kita bisa

menulis tipe data primitif, kemudian

membacanya kembali tanpa harus

diformat sendiri

OutputStream

PrintStream

Untuk mengeluarkan output yang sudah

diformat. DataOutputStream hanya

menangani bagaimana data disimpan

sehingga bisa diambil kembali.

PrintStream lebih berkonsentrasi pada

"tampilan", sehingga data yang ditulis

bisa dibaca dengan baik.

OutputStream dengan

tambahan opsi boolean

untuk memerintahkan

buffer akan

dikosongkan (flush)

setiap kali baris baru

ditulis.

BufferedOutputStream

Digunakan untuk menghindari

penulisan langsung dari media secara

fisik setiap kali perintah write()

OutputStream dengan

kemungkinan

menentukan besar

Page 303: Java With Eclipse

diberikan. Atau dengan kata lain

"gunakan buffer" untuk baca tulis. Kita

bisa menggunakan perintah flush()

untuk mengosongkan buffer dan

mengirimkan hasilnya ke media fisik.

buffer sendiri

File

Data dan program pada memori komputer hanya bisa bertahan selama komputer itu nyala.

Untuk tempat penyimpanan yang lebih lama, komputer menggunakan file, yaitu kumpulan

data yang disimpan dalam hard disk, disket atau CD-ROM, USB stick, dan lain-lain. File

disusun dalam direktori (atau sering juga disebut folder). Direktori bisa terdiri dari

direktori lain atau file lain. Nama direktori dan file digunakan untuk mencari suatu file

dalam komputer.

Program dapat membaca data dari file yang sudah ada. Program juga bisa membuat file

baru atau menulis data ke dalam file yang sudah ada. Dalam Java, input dan output seperti

ini bisa menggunakan aliran (stream). Data karakter yang bisa dibaca manusial dapat

dibaca dari file dengan menggunakan objek dari kelas FileReader yang merupakan kelas

turunan Reader. Data bisa ditulis dalam bentuk yang bisa dibaca manusia dengan

menggunakan FileWriter yang merupakan kelas turunan dari Writer.

Untuk membaca atau menyimpan suatu file dalam format mesin, kelas I/O-nya adalah

FileInputStream dan FileOutputStream. Semua kelas ini didefinisikan dalam paket

java.io.

Perlu dicatat bahwa applet yang didownload dari suatu jaringan pada umumnya tidak bisa

mengakses file karena pertimbangan keamanan. Kita bisa mendownload dan menjalankan

applet, yaitu dengan mengunjungi halaman web pada browser kita. Jika applet tersebut bisa

digunakan untuk mengakses file pada komputer kita, maka orang bisa membuat applet

untuk menghapus semua file dalam komputer yang mendownloadnya.

Untuk mencegah hal seperti itu, ada beberapa hal di mana applet yang didownload tidak

bisa lakukan. Mengakses file adalah salah satu hal yang dilarang. Akan tetapi program

desktop bisa memiliki akses ke file kita seperti program-program lainnya. Program desktop

bisa melakukan akses file yang dijelaskan pada bagian ini.

Kelas FileReader memiliki konstruktor yang mengambil nama file sebagai parameternya,

kemudian membuat aliran input yang bisa digunakan untuk membaca file tersebut.

Page 304: Java With Eclipse

Konstruktor ini akan melemparkan pengecualian bertipe FileNotFoundException jika file

tersebut tidak ditemukan.

Jenis pengecualian seperti ini membutuhkan penanganan wajib, sehingga kita harus

memanggil konstruktor di dalam pernyataan try atau menambahkan pernyataan throw di

kepala subrutin yang menjalankan konstruktor tersebut. Milsanya, anggap kita memiliki

file bernama "data.txt", dan kita ingin membuat program untuk membaca data pada file

tersebut. Kita bisa menggunakan pernyataan berikut untuk membaca aliran input dari file

tersebut :

// (Mendeklarasikan variabel sebelum pernyataan try

// jika tidak, maka variabel tersebut hanya bisa

// dilihat di dalam blok try, dan kita tidak bisa

// menggunakannya lagi di bagian program lain

FileReader data;

try {

// buat aliran input

data = new FileReader("data.txt");

}

catch (FileNotFoundException e) {

... // lakukan sesuatu untuk menangani kesalahan

}

Kelas FileNotFoundException merupakan kelas turunan dari IOException, sehingga

kita bisa menangkap IOException pada pernyataan try...catch di atas. Secara umum,

hampir semua kesalahan yang terjadi pada saat operasi input/output dapat ditangkap

dengan pernyataan catch yang menangani IOException.

Begitu kita berhasil membuat FileReader, kita bisa mulai membacanya. Tapi karena

FileReader hanya memiliki metode input primitif dari standar kelas Reader, kita mungkin

akan perlu membungkusnya dalam objek lain, misalnya BufferedReader atau kelas

pembungkus lain. Untuk membuat BufferedReader untuk membaca file bernama

"data.dat", kita bisa gunakan :

TextReader data;

try {

data = new BufferedReader(new FileReader("data.dat"));

}

catch (FileNotFoundException e) {

... // tangani pengecualian

}

Page 305: Java With Eclipse

BufferedReader memiliki metode bantu untuk mengambil data per baris dengan perintah

readline(). Sehingga apabila satu data ditulis dalam urutan per baris, kita bisa gunakan

perintah Double.parseDouble(string) atau Integer.parseInt(string) untuk mengubahnya

menjadi double atau int.

Untuk menyimpan data tidaklah lebih sulit dari ini. Kita bisa membuat objek bertipe

FileWriter. Dan kemudian kita mungkin ingin membungkus aliran output ini dalam

objek PrintWriter. Misalnya, kita ingin menyimpan data ke file yang bernama

"hasil.dat", kita bisa menggunakan :

PrintWriter result;

try {

keluaran = new PrintWriter(new FileWriter("hasil.dat"));

}

catch (IOException e) {

... // tangani pengecualian

}

Jika tidak ada file bernama "hasil.dat", maka file baru akan dibuat. Jika file sudah ada,

maka isinya akan dihapus dan diganti dengan data yang ditulis oleh program kita.

Pengecualian IOException bisa terjadi jika, misalnya, file tersebut sedang dibaca oleh

program lain, sehingga sistem operasi menolak program kita untuk menulisnya pada saat

yang sama.

Setelah kita selesai menggunakan file, sebaiknya anda menutup file tersebut, atau

mengatakan kepada sistem operasi bahwa kita telah selesai menggunakan file itu (Jika kita

lupa, sistem operasi akan menutup file secara otomatis setelah program selesai dijalankan

atau objek aliran file diambil oleh pemulung memori, akan tetapi akan sangat baik jika kita

menutup file secara manual untuk menghindari kemungkinan lainnya).

Kita bisa menutup file dengan menggunakan metode close() pada aliran tersebut. Setelah

file telah ditutup, maka kita tidak mungkin lagi membaca atau menulis data dari atau ke

file tersebut. Kita harus membukanya kembali. (Perlu dicatat bahwa penutupan file juga

bisa melemparkan pengecualian IOException yang wajib ditangani, akan tetapi

PrintWriter menangani pengecualian tersebut secara otomatis sehingga kita tidak perlu

menanganinya lagi).

Sebagai contoh komplit, berikut ini adalah program yang akan membaca angka dari file

bernama "data.dat", dan kemudian menuliskannya kembali dalam urutan terbalik ke dalam

file yang bernama "hasil.dat". Dalam file tersebut hanya akan ada satu angka untuk setiap

Page 306: Java With Eclipse

barisnya dan diasumsikan tidak ada lebih dari 1000 angka sekaligus. Penanganan

pengecualian digunakan untuk mengecek apakah ada masalah di tengah operasi. Meskipun

mungkin tidak begitu berguna untuk aplikasi sungguhan, akan tetapi program ini

mendemonstrasikan bagaimana menggunakan operasi baca tulis sederhana pada file.

package balikfile;

import java.io.*;

public class BalikFile {

/**

* @param args

*/

public static void main(String[] args) {

BufferedReader data; // Aliran input karakter untuk membaca data

PrintWriter hasil; // Aliran output karakter untuk menulis data

// Array untuk menampung semua angka dari dalam file

double[] angka = new double[1000];

int banyakAngka; // Banyaknya angka yg disimpan dlm array

try { // Buat aliran input

data = new BufferedReader(new FileReader("data.dat"));

}

catch (FileNotFoundException e) {

System.out.println("Tidak bisa menemukan data.dat!");

return; // akhiri program

}

try { // Membuat aliran output

hasil = new PrintWriter(new FileWriter("hasil.dat"));

}

catch (IOException e) {

System.out.println("Tidak bisa membuka hasil.dat!");

System.out.println(e.toString());

try {

data.close(); // Tutup file input

}

catch (IOException f) {

Page 307: Java With Eclipse

System.out.println("Tidak bisa menutup data.dat");

}

return; // End the program.

}

String baris = null; // variabel untuk menyimpan satu baris teks

try {

// Baca data dari file input

banyakAngka = 0;

while ((baris = data.readLine()) != null) { // baca hingga

habis

angka[banyakAngka] = Double.parseDouble(baris);

banyakAngka++;

}

// Tulis hasilnya dalam urutan terbalik

for (int i = banyakAngka-1; i >= 0; i--)

hasil.println(angka[i]);

System.out.println("Selesai!");

}

catch (IOException e) {

// Ada masalah dengan pembacaan/penulisan file

System.out.println("Kesalahan baca/tulis");

}

catch (NumberFormatException e) {

// Ada masalah dengan format angka dalam file

System.out.println("Kesalahan format: " + e.getMessage());

}

catch (IndexOutOfBoundsException e) {

// Tidak boleh meletakkan 1000 angka dalam file

System.out.println("Terlalu banyak angka.");

System.out.println("Penulisan dihentikan.");

}

finally {

// Akhiri dengan menutup semua file apapun yang terjadi

try {

data.close(); // Tutup file input

}

Page 308: Java With Eclipse

catch (IOException e) {

System.out.println("Tidak bisa menutup data.dat");

}

hasil.close(); // Tutup file output

}

}

}

Berikut ini adalah program lengkapnya yang bisa diimport ke dalam Eclipse beserta contoh

file data.dat.

Setelah selesai dijalankan file baru akan dibuat hasil.dat yang bisa Anda double-click

untuk melihat hasilnya

Nama File, Direktori, dan Kelas File

Topik tentang nama file sebenarnya lebih kompleks daripada yang telah kita bahas. Untuk

menunjuk pada sebuah file, kita harus memberikan bukan hanya nama file, tapi juga nama

direktori di mana file tersebut disimpan. Nama file sederhana seperti "data.dat" atau

"hasil.dat" diambil dengan mengacu pada direktori sekarang (current directory, atau juga

disebut direktori kerja). Pada program di bagian sebelumnya, hasil.dat disimpan pada

direktori yang sama dengan direktori utama pada proyek balikfile.

File yang tidak diletakkan pada direktori kerja harus diberikan nama "path", atau nama

lengkap termasuk nama direktorinya. Untuk memperjelas lagi, ada dua jenis nama path,

yaitu nama path absolut dan nama path relatif. Nama path absolut memiliki informasi

lengkap dari akar direktorinya, misalnya "C:\workspace\balikfile\data.dat". Sedangkan

nama path relatif adalah nama file yang dihitung mulai dari direktori aktifnya.

Sayangnya, sintaks untuk nama path dan file bervariasi dari satu sistem ke sistem lain.

Misalnya "

data.dat -- pada komputer apapun, yaitu file data.dat pada direktori aktif.

Page 309: Java With Eclipse

/home/lyracc/java/contoh/data.dat -- Nama path absolut pada sistem operasi

LINUX atau UNIX. Mengacu pada file bernama data.dat di direktori yang ditunjuk.

C:\lyracc\java\contoh\data.dat -- Nama path absolut pada DOS atau Windows

Hard Drive:java:contoh:data.dat -- Misalnya "Hard Drive" adalah nama dari

drivenya, maka ini adalah nama path absolut pada Macintosh OS 9

contoh/data.dat -- nama path relatif pada LINUX atau UNIX. "contoh" adalah nama

direktori yang terdapat pada direktori aktif, dan data.dat adalah file dalam direktori

tersebut. Pada Windows, nama path relatifnya menjadi contoh\data.dat dan pada

Macintosh menjadi contoh:data.dat.

Untuk mencegah berbagai masalah yang mungkin muncul karena beragam sistem ini, Java

memiliki kelas bernama java.io.File. Objek bertipe kelas ini melambangkan suatu file.

Lebih tepatnya, objek bertipe File melambangkan nama file, bukan file itu sendiri. Nama

yang ditunjuk belum tentu ada. Direktori juga dianggap Java sebagai File, sehingga File

juga melambangkan nama direktori sekaligus nama file.

Objek File memiliki konstruktor new File(String) yang akan membuat objek File dari

namanya. Nama tersebut bisa nama sederhana, nama path relatif, atau nama path absolut.

Misalnya new File("data.dat") membuat objek File dari file bernama data.dat di

direktori aktif.

Konstruktor lain memiliki konstruktor new File(File,String), di mana parameter

pertama adalah direktori di mana file tersebut berada, dan parameter kedua adalah nama

filenya.

Objek File memiliki beberapa metode instansi. Misalnya file adalah variabel bertipe

File, berikut ini adalah beberapa metodenya :

file.exists() -- mengembalikan nilai boolean, yang jika true maka file tersebut

ada. Kita bisa menggunakan perintah ini misalnya untuk mencegah menulis file

yang sama ketika kita membuka objek FileWriter baru.

file.isDirectory() -- mengembalikan nilai boolean yang mengembalikan true

jika objek File adalah suatu direktori, dan false jika File adalah file biasa, atau

tidak ada file dengan nama tersebut.

file.delete() -- menghapus file jika ada

file.list() -- jika objek File adalah suatu direktori, maka fungsi ini

mengembalikan array bertipe String[] yang berisi nama-nama file pada direktori

tersebut. Jika tidak, maka kembaliannya adalah null.

Page 310: Java With Eclipse

Berikut ini adalah contoh program yang menulis isi file di dalam direktori yang diinput

dari user :

package daftardirektori;

import java.io.*;

public class DaftarDirektori {

/* Program ini mengembalikan isi suatu direktori

* User memasukkan direktori yang ingin dilihat

* Jika direktori tidak ada, maka pesan kesalahan

* akan ditulis dan program akan berhenti

*/

public static void main(String[] args) {

String namaDirektori = null; // Nama direktori dari user

File direktori; // objek File yang mengacu pada direktori

String[] isiFile; // Array berisi file pada direktori

// buat objek baru untuk mengambil input

BufferedReader br = new BufferedReader(new

InputStreamReader(System.in));

System.out.print("Masukkan nama direktori : ");

try {

namaDirektori = br.readLine();

} catch(IOException ioe) {

System.out.println("Kesalahan IO terjadi");

System.exit(1);

}

direktori = new File(namaDirektori);

if (direktori.isDirectory() == false) {

if (direktori.exists() == false)

System.out.println("Tidak ada direktori ini!");

else

System.out.println("Ini bukan direktori.");

}

else {

Page 311: Java With Eclipse

isiFile = direktori.list();

System.out.println("Files dalam direktori \"" + direktori +

"\":");

for (int i = 0; i < isiFile.length; i++)

System.out.println(" " + isiFile[i]);

}

}

}

Berikut ini adalah program lengkapnya yang bisa diimport ke dalam Eclipse. Ini adalah

hasil keluarannya :

Semua kelas yang digunakan untuk memaca dan menulis data dari dan ke dalam file

memiliki konstruktor yang bisa mengambil objek File sebagai parameternya. Misalnya,

jika file adalah variabel bertipe File, dan kita ingin mengambil karakter dari file

tersebut, maka kita bisa membuat FileReader untuk melakukannya dengan menggunakan

new FileReader(file).

Mengkopi File

Mengkopi suatu file adalah operasi biasa, dan sistem operasi manapun memiliki perintah

atau cara untuk melakukannya. Akan tetapi kita juga bisa membuat program Java untuk

melakukannya.

Karena program harus bisa mengkopi file jenis apapun, kita tidak bisa menganggap data di

dalam file adalah data yang bisa dibaca manusia. File lagu atau video misalnya berisi

deretan byte yang merupakan representasi digital dari lagu atau video tersebut.

Page 312: Java With Eclipse

Oleh karena itu kita harus menggunakan InputStream dan OutputStream untuk

melakukan operasi baca tulis yang bisa menangani data biner, bukan Reader dan Writer

yang hanya bisa menangani data yang bisa dibaca manusia.

Program yang kita buat akan mengkopi beberapa byte sekaligus dari InputStream ke

OutputStream, akan tetapi kita membutuhkan tempat sementara di mana data tersebut

akan ditempatkan sebelum data tersebut ditulis kembali pada OutputStream. Tempat

sementara tersebut disebut buffer yang merupakan array berukuran tertentu, misalnya 4096

byte (atau 4 kilo byte).

Jika sumber adalah variabel bertipe InputStream, maka byteTerbaca =

sumber.read(buffer) akan mengisi penuh buffer. Metode ini mengembalikan int yang

merupakan berapa byte yang efektif diambil oleh sumber, kemudian diletakkan dalam

variabel byteTerbaca. Jika hasilnya -1, berarti tidak ada lagi data yang bisa diambil dari

dalam sumber.

Begitu juga jika kopi adalah keluaran yang bertipe OutputStream maka

kopi.write(buffer, 0, byteTerbaca) menulis deretan byte dari buffer dari posisi 0

hingga byteTerbaca ke aliran keluaran kopi.

Sehingga secara umum perintah-perintah di atas dapat dirangkum menjadi :

byte[] buffer = new byte[4096];

int byteTerbaca;

while((byteTerbaca = sumber.read(buffer)) != -1)

kopi.write(buffer, 0, byteTerbaca);

Perintah kopi-file pada sistem operasi baik DOS/Windows atau LINUX/UNIX

menggunakan perintah pada konsol yang menambahkan file sumber dan file tujuannya.

Misalnya, pada konsol Windows, kita bisa menggunakan "copy awal.dat akhir.dat"

untuk mengkopi file awal.dat ke file bernama akhir.dat.

Tambahan parameter pada konsol ini disebut argumen baris perintah. Argumen baris

perintah ini bisa juga digunakan dalam program Java. Dalam Java argumen baris perintah

ini diisi dalam array String[] bernama args, yang kemudian dimasukkan sebagai parameter

dalam subrutin main(). Ingat bagaimana "biasanya" subrutin main() dideklarasikan

sebagai public static void main(String[] args).

Pada program Java yang sudah dikompilasi, kita bisa memanggilnya dengan "java

KopiFile awal.dat akhir.dat" jika KopiFile adalah nama kelas yang akan kita buat

Page 313: Java With Eclipse

untuk mengkopi file. args[0] akan berisi awal.dat sedangkan args[1] akan berisi

akhir.dat.

Program yang akan kita buat menerima input dari baris perintah. Kemudian program akan

mengecek apakah kedua parameter tersebut berisi nama file dengan benar. Jika salah satu

parameternya kosong, maka program akan menampilkan pesan kesalahan. Program juga

akan mengecek apakah akhir.dat merupakan file yang sudah ada sebelumnya, kemudian

memberi pertanyaan kepada user apakah isi file ini ingin ditindih dengan isi file awal.dat.

Jika ya, maka operasi akan diteruskan, jika tidak maka program akan dihentikan.

Berikut ini adalah listing lengkap program KopiFile, yang bisa diunduh di sini dan

diimport ke dalam Eclipse.

import java.io.*;

public class KopiFile {

/**

* @param args

*/

public static void main(String[] args) {

// Mengecek apakah argumen program cukup untuk meneruskan program

// Dibutuhkan dua argumen, yaitu sumberFile dan tujuanFile

if (args.length < 2) {

System.out.println("Cara menjalankan program : " +

"java KopiFile sumberFile tujuanFile");

return;

}

String sumberNamaFile = args[0];

String tujuanNamaFile = args[1];

File sumberFile = new File(sumberNamaFile);

File kopiFile = new File(tujuanNamaFile);

// Jika kopi file sudah ada, kita akan tanyakan apakah file

tujuan

// akan ditimpa

if (kopiFile.exists()) {

// buat objek baru untuk mengambil input

BufferedReader br = new BufferedReader(new

InputStreamReader(System.in));

Page 314: Java With Eclipse

String timpaFile = null;

System.out.print("Apakah Anda ingin menimpa " +

tujuanNamaFile + " ? (y/t) ");

try {

timpaFile = br.readLine();

} catch(IOException ioe) {

System.out.println("Kesalahan IO terjadi");

System.exit(1);

}

// jika jawabannya tidak, hentikan program

if (timpaFile.equalsIgnoreCase("t"))

return;

}

// Di sini kita siap untuk mengkopi file

// Buat aliran input dan output

FileInputStream sumber = null;

try {

sumber = new FileInputStream(sumberFile);

} catch (FileNotFoundException e) {

System.out.println("File sumber tidak ada, berupa direktori "

+

"atau tidak bisa dibuka, program dihentikan!");

return;

}

FileOutputStream kopi = null;

try {

kopi = new FileOutputStream(tujuanNamaFile);

} catch (FileNotFoundException e) {

System.out.println("File tujuan tidak valid atau tidak bisa

ditulis, " +

"program dihentikan!");

return;

}

byte[] buffer = new byte[4096];

int byteTerbaca;

Page 315: Java With Eclipse

try {

while((byteTerbaca = sumber.read(buffer)) != -1)

kopi.write(buffer, 0, byteTerbaca);

} catch (IOException e) {

System.out.println("Ada masalah di tengah pengkopian

program");

return;

}

System.out.println("Kopi file selesai dijalankan!");

}

}

Perlu diingat bahwa program ini tidak bisa dijalankan lewat Eclipse. Jika Anda mencoba

menjalankan lewat Eclipse, maka tampilan kesalahan akan muncul, karena tidak ada

parameter yang diberikan.

Untuk menjalankan program, Anda harus membuka konsol pada Windows dengan Start ->

Run -> kemudian ketik cmd dan enter. Setelah itu pergi ke direktori tempat proyek Anda

berada pada Eclipse. Misalnya pada komputer saya, saya meletakkan semua proyek

Eclipse pada c:\belajarjava.lyracc.com\KopiFile. Di dalamnya seharusnya Anda

akan menemui 2 direktori, yaitu src dan bin. src adalah tempat di mana kode sumber

berada, sedangkan bin adalah tempat dimana hasil kompilasi berada. Eclipse akan

melakukan kompilasi secara otomatis.

Berikut screenshot hasil jalannya program. Di sini saya mengkopi file dari

c:\belajarjava.lyracc.com\KopiFile\src\KopiFile.java ke

c:\belajarjava.lyracc.com\Kopi123.java.

Page 316: Java With Eclipse

Jaringan (network)

Dalam pemrograman, jaringan (network) hanyalah salah satu jenis dari input di mana data

bisa diambil, dan output di mana data bisa dikirim. Konsep ini mempermudah pemahaman

kita tentang pemrograman dalam jaringan, akan tetapi ada beberapa hal lain yang harus

diperhatikan sehingga pemrograman pada jaringan dapat berhasil dengan baik.

Pada Java, kita bisa menggunakan aliran input dan output untuk melakukan komunikasi

pada network, seperti halnya pada file. Akan tetapi membuat koneksi jaringan antara dua

komputer sedikit lebih rumit, karena ada dua komputer yang berbeda, yang keduanya harus

setuju membuka koneksi. Dan ketika data dikirimkan dari satu komputer ke komputer lain,

komunikasi harus dilakukan seirama sehingga data yang dikirimkan akan sampai ke

komputer yang lain.

Salah satu paket Java standar adalah java.net. Paket ini memiliki beberapa kelas yang

bisa digunakan untuk berkomunikasi melalui jaringan. Dua jenis I/O network disediakan

dalam paket ini. Yang pertama, yang lebih tinggi tingkatannya, berdasarkan pada Web dan

memberikan fasilitas komunikasi seperti halnya web browser ketika mendownload suatu

halaman web untuk kemudian ditampilkan. Kelas utama dalam jenis network seperti ini

adalah java.net.URL dan java.net.URLConnection. Suatu objek bertipe URL adalah

lambang abstrak dari sebuah URL (Universal Resource Locator), yaitu alamat web di mana

dokumen HTML atau lainnya bisa ditemukan pada web. Sedangkan URLConnection

adalah koneksi network ke dokumen tadi.

Page 317: Java With Eclipse

Jenis I/O kedua adalah melihat jaringan pada tingkatan yang lebih rendah, yaitu

berdasarkan ide suatu soket (socket). Soket digunakan oleh program untuk melakukan

koneksi dengan program lain pada suatu jaringan. Komunikasi melalui network melibatkan

dua soket, yaitu masing-masing pada setiap komputer. Java memiliki kelas

java.net.Socket untuk merepresentasikan suatu soket yang digunakan dalam

komunikasi network.

Istilah "soket" mungkin mirip dengan colokan kabel data (misalnya) modem, akan tetapi

penting untuk diingat bahwa soket adalah objek bertipe Socket. Artinya program bisa

memiliki beberapa soket dalam waktu yang sama, yang masing-masing terhubung ke

program yang dijalankan pada komputer lain. Semuanya menggunakan koneksi network

yang sama dari satu kabel.

Bagian ini akan memberikan pengenalan tentang kelas-kelas dasar jaringan, dan

bagaimana hubungannya dengan aliran input dan ouput serta pengecualian.

URL dan URLConnection

Kelas URL digunakan untuk merepresentasikan suatu sumber pada Web. Setiap sumber

memiliki alamat, yang unik (tidak bisa sama), dan memiliki informasi yang cukup

sehingga web browser bisa mencari sumber tersebut dan mengambilnya. Alamat ini

disebut "url" atau "universal resource locator".

Suatu objek beritpe kelas URL melambangkan alamat tersebut. Jika kita sudah memiliki

objek bertipe URL, maka kita bisa membuka URLConnection ke alamat tersebut. Suatu url

biasanya berupa string, misalnya "http://java.lyracc.com/belajar/java-untuk-

pemula/bab-i-pendahuluan". Ada juga yang disebut url relatif. URL relatif adalah lokasi

suatu sumber relatif terhadap url lain, yang biasanya disebut landasan (base) atau konteks

(context) dari url relatif tersebut. Misalnya jika konteksnya adalah

http://java.lyracc.com/belajar/java-untuk-pemula/ maka url relatif dari "bab-i-

pendahuluan" akan menunjuk pada http://java.lyracc.com/belajar/java-untuk-

pemula/bab-i-pendahuluan.

Suatu objek bertipe URL bukan string sederhana, akan tetapi dibangun dari kumpulan string

yang membentuk suatu url. Objek URL juga bisa dibuat dari objek URL lain, yang

merupakan konteksnya, dan string lain yang berisi relatif urlnya. Konstruktornya memiliki

bentuk seperti :

public URL(String alamatURL) throws MalformedURLException

dan

Page 318: Java With Eclipse

public URL(URL konteks, String alamatRelatif) throws

MalformedURLException

Lihat bahwa kedua konstruktor akan melempar pengecualian bertipe

MalformedURLException jika string yang diberikan bukan nama url legal. Kelas

MalformedURLException merupakan kelas turunan dari IOException yang wajib

ditangani, sehingga konstruktor di atas harus dipanggil dalam pernyataan try ... catch

atau ditulis di dalam subrutin yang melempar pengecualian ini.

Konstruktur jenis kedua akan lebih nyaman digunakan untuk applet. Dalam applet, tersedia

dua metode yang bisa digunakan untuk mengambil konteks URL. Metode

getDocumentBase() pada kelas Applet mengembalikan objek bertipe URL. Objek URL ini

adalah lokasi tempat halaman HTML yang berisi applet tersebut berada. Dengan ini, kita

bisa memerintahkan applet untuk kembali dan mengambil file lain yang disimpan di

tempat yang sama. Misalnya,

URL url = new URL(getDocumentBase(), "data.txt");

membuat URL baru yang merujuk pada file bernama data.txt pada komputer yang sama

dan pada direktori yang sama pada halaman web di mana applet tersebut sedang berjalan.

Metode lainnya, yaitu getCodeBase(), mengembalikan URL yang merupakan lokasi di

mana applet tersebut berada (belum tentu sama dengan lokasi HTML-nya).

Setelah kita memiliki objek URL yang benar, kita bisa memanggil openConnection()

untuk membuka koneksi pada URL tersebut. Metode ini mengembalikan objek bertipe

URLConnection. Objek URLConnection bisa digunakan untuk membuka InputStream

untuk membaca halaman atau file pada alamat URL tersebut, yaitu dengan menggunakan

metode getInputStream(). Misalnya :

URL url = new URL(alamatURL);

URLConnection koneksi = url.openConnection();

InputStream dataURL = connection.getInputStream();

Metode openConnection() dan getInputStream dapat melempar pengecualian

IOException. Jika InputStream berhasil dibuka, kita bisa menggunakannya dengan cara

biasa, termasuk membungkusnya dalam aliran input jenis lain, misalnya BufferedReader.

Membaca dari aliran ini tentunya juga bisa melemparkan pengecualian.

Salah satu metode instansi yang berguna dalam kelas URLConnection adalah

getContentType(), yang mengembalikan String yang menjelaskan jenis informasi pada

URL yang ditunjuk. Hasilnya bisa bernilai null jika jenisnya belum diketahui, atau tidak

bisa ditentukan. Jenis dokumen bisa saja belum tersedia hingga aliran input berhasil dibuat,

Page 319: Java With Eclipse

sehingga lebih baik menggunakan getContentType() setelah getInputStream() berhasil

dilakukan.

String yang dikembalikan oleh getContentType() ditulis dalam format yang disebut

MIME, misalnya "text/plain", "text/html", "image/jpeg", "image/gif", dan banyak lagi

lainnya. Semua jenis MIME terdiri dari dua bagian, yaitu bagian umum, seperti "text" atau

"image", dan bagian khususnya, misalnya "html" atau "gif". Jika kita hanya tertarik pada

data teks misalnya, kita hanya perlu menguji apakah hasil keluaran getContentType()

dimulai dengan "text". (Jenis MIME pertama kali dimaksudkan untuk menjelaskan isi

email. Namanya adalah singkatan dari "Multipurpose Internet Mail Extensions". Kini,

MIME digunakan secara umum untuk menjelaskan jenis suatu informasi atau file pada

suatu sumber).

Mari kita lihat contoh singkat bagaimana membaca data dari suatu URL. Subrutin berikut

akan membuka koneksi ke URL tertentu, mengecek apakah jenisnya berupa teks,

kemudian mengkopi hasilnya ke layar. Beberapa operasi dalam subrutin ini mungkin

melempar pengecualian. Kita akan menambahkan "throws Exception" di kepala subrutin

untuk meneruskan penanganan pengecualian ini kepada program utama yang memanggil

subrutin ini.

static void bacaTeksDariURL( String alamatURL ) throws Exception {

// Subrutin ini mencetak isi dari alamat URL yang

// diberikan ke layar. Semua kesalahan akan ditangani

// oleh program yang memanggil subrutin ini

/* Buka koneksi ke URL, dan ambil aliran input

* untuk membaca data dari URL. */

URL url = new URL(alamatURL);

URLConnection koneksi = url.openConnection();

InputStream dataURL = koneksi.getInputStream();

/* Cek apakah konten bertipe teks */

String jenisKonten = koneksi.getContentType();

if (jenisKonten == null || jenisKonten.startsWith("text") ==

false)

throw new Exception("URL tidak bertipe teks.");

/* Kopi karakter dari aliran input ke layar

Page 320: Java With Eclipse

* hingga akhir file ditemukan (atau kesalahan ditemui) */

while (true) {

int data = dataURL.read();

if (data < 0)

break;

System.out.print((char)data);

}

} // akhir bacaTeksDariURL()

Soket, Klien, dan Server

Komunikasi melalui internet dilakukan berdasarkan sepasang protokol yang dinamakan

Internet Protocol dan Transmission Control Protocol, yang digabungkan menjadi TCP/IP.

(Sebenarnya, ada lagi protokol komunikasi yang lebih sederhana yang disebut dengan UDP

yang bisa digunakan menggantikan TCP pada beberapa aplikasi. UDP juga didukung Java,

akan tetapi kita akan membahas TCP/IP saja yang merupakan komunikasi dua arah yang

handal digunakan pada beberapa komputer melalui jaringan).

Agar dua program dapat berkomunikasi menggunakan TCP/IP, masing-masing program

harus membuat soket, yang kemudian soket-soket tersebut harus terhubung satu sama lain.

Setelah terhubung, komunikasi dapat dilakukan dengan menggunakan aliran input dan

output seperti biasa. Setiap program harus memiliki aliran input dan outputnya masing-

masing. Data yang ditulis oleh suatu program di aliran outputnya akan dikirim ke komputer

lain. Di sana, data tersebut akan diisi pada aliran input program tersebut. Ketika program

tadi membaca aliran inputnya, maka pada dasarnya program tersebut membaca data yang

dikirim oleh program lain.

Bagian tersulitnya adalah bagaimana membuat koneksi antar komputer tersebut. Dalam hal

ini, dua soket akan digunakan. Pertama-tama, suatu program harus membuat soket yang

menunggu secara pasif hingga koneksi lain dari soket lain di komputer lain datang. Soket

yang sedang menunggu ini disebut sedang "mendengar" (listening) suatu koneksi.

Di sisi lain di komputer lain, program lain membuat soket yang mengirim permintaan

sambungan ke soket pendengar tadi. Ketika soket pendengar menerima permintaan

sambungan dari soket lain, soket ini akan merespon, sehingga komunikasi akan terjadi.

Begitu komunikasi terjadi, maka masing-masing program akan bisa membuat aliran input

dan aliran output untuk koneksi ini. Komunikasi akan terus terjadi hingga salah satu

program menutup (close) koneksi.

Page 321: Java With Eclipse

Program yang membuat soket pendengar, juga sering disebut server, dan soketnya disebut

soket server. Program yang menghubungi server disebut klien (client), dan soket yang

digunakan disebut soket klien.

Idenya adalah suatu server di suatu tempat pada network sedang menunggu permintaan

sambungan dari suatu klien. Server dianggap sebagai sesuatu yang memberikan layanan,

dan klien mendapatkan layanan dengan cara menyambungkannya pada server. Pada

komunikasi jaringan, ini disebut model klien/server.

Dalam aplikasi dunia nyata, program server dapat memberikan koneksi kepada beberapa

klien pada waktu yang sama. Ketika suatu klien terhubung pada soket pendengar, maka

soket tersebut tidak berhenti mendengar. Akan tetapi, soket tersebut akan terus mendengar

jika ada koneksi klien lain pada saat yang sama.

Kelas URL yang telah didiskusikan sebelumnya menggunakan soket klien di belakang layar

untuk melakukan komunikasi jaringan yang dibutuhkan. Di sisi lainnya adalah program

server yang menerima permintaan sambungan dari objek URL, membaca permintaan objek

tersebut, misalnya permintaan file di alamat tertentu, dan meresponnya dengan

mengirimkan isi file tersebut melalui network ke objek URL tadi. Setelah mengirimkan

data, server akan memutuskan koneksi ini.

Untuk mengimplementasikan koneksi TCP/IP, paket java.net menyediakan dua kelas,

yaitu ServerSocket dan Socket. Objek bertipe ServerSocket melambangkan soket

pendengar yang menunggu permintaan sambungan dari klien. Objek bertipe Socket

melambangkan sisi lain dari suatu sambungan, yang bisa berarti soket klien, atau bisa saja

soket lain yang dibuat server untuk menangani permintaan dari klien. Dengan cara ini

server bisa membuat beberapa soket dan menangani beberapa koneksi sekaligus. (Suatu

ServerSocket sendiri tidak berpartisipasi langsung pada koneksi itu sendiri; ia hanya

bertugas untuk mendengarkan permintaan sambungan, dan membuat Socket untuk

melakukan koneksi yang sesungguhnya)

Untuk menggunakan Socket dan ServerSocket, kita harus tahu tentang alamat Internet.

Program klien harus bisa menentukan komputer mana yang akan berkomunikasi

dengannya. Setiap komputer pada internet memiliki alamat IP yang merupakan alamat unik

setiap komputer di dalam internet. Komputer juga bisa memiliki nama domain seperti

www.yahoo.com atau www.google.com.

Page 322: Java With Eclipse

Suatu komputer bisa memiliki beberapa program untuk melakukan komunikasi network

secara bersamaan, atau satu program mungkin berkomunikasi dengan beberapa komputer

sekaligus. Agar bisa bekerja seperti ini, soket sebenarnya merupakan kombinasi antara

alamat IP dan nomor port. Nomor port hanya merupakan bilangan bulat 16-bit (dari 0

hingga 216 - 1). Suatu server tidak hanya mendengar koneksi saja, akan tetapi ia mendengar

koneksi dari port tertentu.

Klien yang ingin berkomunikasi dengan server harus mengetahui alamat Internet komputer

beserta nomor port di mana server tersebut mendengarkan permintaan sambungan. Server

web, misalnya, pada umumnya mendengarkan koneksi pada port 80; layanan internet lain

juga memiliki nomor port standar. (Nomor port standar adalah nomor di bawah 1024. Jika

kita membuat program server sendiri, kita sebaiknya menggunakan port benomor lebih

besar dari 1024).

Ketika kita membuat objek bertipe ServerSocket, kita harus memberikan nomor port

yang akan didengar oleh server. Konstruktornya memiliki bentuk seperti

public ServerSocket(int port) throws IOException

Setelah ServerSocket berhasil dijalankan, ia akan mulai mendengarkan permintaan

sambungan. Metode accept() dalam kelas ServerSocket akan menerima permintaan

tersebut, kemudian mempersiapkan sambungan dengan klien, dan mengembalikan objek

Socket yang bisa digunakan untuk berkomunikasi dengan klien. Metode accept()

memiliki bentuk seperti

public Socket accept() throws IOException

Ketika kita memanggil metode accept(), ia tidak akan mengembalikan hasilnya sebelum

permintaan sambungan diterima (atau suatu kesalahan terjadi). Metode ini disebut

"diblokade" ketika menunggu koneksi. (Ketika suatu metode diblokade, maka thread yang

memanggil metode tersebut tidak bisa berbuat apa-apa. Akan tetapi thread lain di program

yang sama masih bisa berjalan). ServerSocket tersebut akan terus mendengar koneksi

hingga ia ditutup menggunakan metode close() atau hingga terjadi kesalahan.

Misalnya kita akan membuat server yang akan mendengarkan port 1728, dan misalnya kita

telah menulis metode baru beriLayanan(Socket) untuk menangani komunikasi dengan

suatu klien. Maka bentuk sederhana dari program server adalah sebagai berikut :

try {

ServerSocket server = new ServerSocket(1728);

while (true) {

Socket koneksi = server.accept();

beriLayanan(koneksi);

Page 323: Java With Eclipse

}

}

catch (IOException e) {

System.out.println("Server dimatikan dengan pesan kesalahan:

" + e);

}

Di sisi klien, soket klien dibuat dengan menggunakan konstruktor pada kelas Socket.

Untuk melakukan koneksi ke server pada suatu alamat dan port tertentu, kita bisa

menggunakan konstruktor

public Socket(String komputer, int port) throws IOException

Parameter pertama bisa berupa alamat IP atau nama domain. Konstruktor akan

memblokadi dirinya hingga koneksi tersambung atau hingga terjadi kesalahan. Setelah

koneksi tersambung, kita bisa menggunakan metode getInputStream() dan

getOutputStream() pada Socket untuk mengambil aliran input dan output yang bisa

digunakan untuk komunikasi antara dua komputer.

Berikut ini adalah kerangka untuk melakukan koneksi klien :

void koneksiKlien(String namaKomputer, int port) {

// namaKomputer bisa berupa alamat IP atau nama domain

// dari komputer yang bertindak sebagai server.

// port adalah port dimana server mendengarkan koneksi,

// misalnya 1728.

Socket koneksi;

InputStream in;

OutputStream out;

try {

koneksi = new Socket(namaKomputer,port);

in = koneksi.getInputStream();

out = koneksi.getOutputStream();

}

catch (IOException e) {

System.out.println(

"Usah melakukan sambungan gagal, dengan kesalahan : " +

e);

return;

}

.

. // Gunakan aliran in dan out untuk berkomunikasi dengan server

.

try {

Page 324: Java With Eclipse

koneksi.close();

// (Atau, bisa juga bergantung pada server untuk

// memutuskan sambungan)

}

catch (IOException e) {

}

} // akhir koneksiKlien()

Membuat komukasi melalui jaringan terlihat lebih mudah dari yang sebenarnya. Jika

jaringan yang kita gunakan benar-benar handal, mungkin perintah di atas cukup untuk

digunakan. Akan tetapi, untuk membuat program tangguh yang bisa menangani segala

permasalahan dalam jaringan yang kurang handal atau karena kesalahan manusia misalnya,

adalah hal yang tidak mudah. Pengalaman yang bisa membawa kita menjadi programmer

jaringan yang lebih baik dan lebih komplet. Yang kita bahas di sini semoga berguna

sebagai pengantar untuk membawa Anda lebih jauh mencari tahu tentang pemrograman

dengan jaringan.

Contoh Pemrograman pada Jaringan

Contoh ini melibatkan dua program, yaitu klien sederhana dan servernya. Klien melakukan

koneksi dengan server, membaca satu baris teks dari server, kemudian menampilkan teks

ini pada layar. Teks yang dikirim oleh server adalah tanggal dan waktu saat ini di komputer

di mana server dijalankan.

Untuk membuka koneksi, klien harus tahu di komputer mana server dijalankan dan di port

mana server tersebut mendengarkan permintaan sambungan. Server akan mendengarkan

pada port bernomor 32007. Nomor port ini bisa berapapun di antara 1025 hingga 65535,

asalkan klien dan servernya menggunakan port yang sama. Nomor port antara 1 hingga

1024 hanya digunakan oleh layanan standar dan seharusnya tidak digunakan untuk server

lainnya.

Nama komputer atau alamat IP di mana server dijalankan harus diberikan pada paramater

baris perintah. Misalnya jika server dijalankan pada komputer kita sendiri, kita bisa

memanggilnya dengan "java KlienTanggal localhost". Berikut ini adalah program klien

lengkapnya.

import java.net.*;

import java.io.*;

public class KlienTanggal {

static final int PORT_PENDENGAR = 32007;

Page 325: Java With Eclipse

/**

* @param args

*/

public static void main(String[] args) {

String komputer; // Nama komputer yang akan disambungkan

Socket koneksi; // Soket untuk berkomunikasi dengan

// komputer tersebut

Reader masuk; // Aliran untuk membaca data dari koneksi

/* Ambil nama komputer dari baris perintah */

if (args.length > 0)

komputer = args[0];

else {

// Tidak ada nama komputer yang diberikan

// Beri pesan kesalahan dan program selesai

System.out.println("Cara menggunakan : java KlienTanggal

<server>");

return;

}

/* Buat koneksi, kemudian baca dan tampilkan di layar */

try {

koneksi = new Socket( komputer, PORT_PENDENGAR );

masuk = new InputStreamReader( koneksi.getInputStream() );

while (true) {

int ch = masuk.read();

if (ch == -1 || ch == '\n' || ch == '\r')

break;

System.out.print( (char)ch );

}

System.out.println();

masuk.close();

}

catch (IOException e) {

System.out.println("Kesalahan : " + e);

}

}

}

Page 326: Java With Eclipse

Perhatikan bahwa semua komunikasi dengan server dilakukan dalam pernyataan try ...

catch. Ini akan menangkap pengecualian IOException yang mungkin terjadi ketika

koneksi sedang dibuka atau ditutup atau sedang membaca karakter dari aliran input.

Aliran yang digunakan adalah aliran sederhana Reader yang memiliki operasi input

masuk.read(). Fungsi ini membaca satu per satu karakter dari aliran, kemudian

mengembalikan nomor kode Unicodenya. Jika akhir aliran telah dicapai, maka nilai -1

akan dikembalikan. Perulangan while membaca karakter ini satu per satu hingga akhir

aliran ditemui atau akhir baris ditemui. Akhir baris ditandai dengan salah satu dari '\n' atau

'\r' atau keduanya, tergantung dari jenis komputer di mana server tersebut berjalan.

Agar program ini dapat berjalan tanpa kesalahan, maka program server harus dijalankan

terlebih dahulu. Kita bisa membuat program klien dan server pada komputer yang sama.

Misalnya kita bisa membuat dua jendela konsol pada windows, kemudian menjalankan

server di konsol yang satu dan menjalankan klien di server yang lain. Agar ini bisa

berjalan, komputer lokal kita memiliki alamat 127.0.0.1, sehingga perintah "java

KlienTanggal 127.0.0.1" artinya sama dengan memerintahkan program KlienTanggal

untuk melakukan sambungan dengan server yang berjalan di komputer yang sama. Atau

bisa juga menggunakan alamat "localhost" sebagai pengganti "127.0.0.1".

Program servernya kita namakan ServerTanggal. Program ServerTanggal membuat

ServerSocket untuk mendengarkan permintaan sambungan pada port 32007. Setelah

soket pendengar kita buat, maka server akan masuk pada perulangan tak hingga di mana ia

menerima dan mengolah permintaan sambungan. Program ini akan berjalan terus menerus

tanpa henti kecuali kita hentikan dengan paksa -- misalnya dengan menekan tombol Ctrl-C

di jendela konsol di mana server dijalankan.

Ketika koneksi diterima dari klien, server akan memanggil subrutin lain untuk menangani

koneksi tersebut. Dalam subrutin itu, pengecualian apapun yang terjadi akan ditangkap

sehingga server tidak akan mati. Subrutin akan membuat aliran PrintWriter untuk

mengirim data melalui koneksi yang terjadi.

Server akan menulis tanggal dan waktu sekarang pada aliran output ini, kemudian menutup

koneksi. (Kelas standar java.util.Date akan digunakan untuk mengambil tanggal saat

ini. Objek bertipe Date melambangkan tanggal dan waktu. Konstruktor standarnya, "new

Date()" membuat objek yang melambangkan tanggal dan waktu ketika objek tersebut

dibuat.)

Berikut ini adalah program server lengkapnya :

import java.net.*;

Page 327: Java With Eclipse

import java.io.*;

import java.util.Date;

public class ServerTanggal {

static final int PORT_PENDENGAR = 32007;

/**

* @param args

*/

public static void main(String[] args) {

ServerSocket pendengar; // Mendengarkan sambungan yang masuk

Socket koneksi; // Untuk berkomunikasi dengan sambungan yang

masuk

/*

* Menerima dan mengolah sambungan selamanya, atau hingga

kesalahan

* terjadi. (Kesalahan yang terjadi ketika sedang berkomunikasi

atau

* mengirimkan tanggal akan ditangkap untuk mencegah server

crash)

*/

try {

pendengar = new ServerSocket(PORT_PENDENGAR);

System.out.println("Mendengarkan pada port " +

PORT_PENDENGAR);

while (true) {

koneksi = pendengar.accept();

kirimTanggal(koneksi);

}

} catch (Exception e) {

System.out.println("Maaf, server telah mati.");

System.out.println("Kesalahan : " + e);

return;

}

}

static void kirimTanggal(Socket klien) {

// Parameternya, klien, adalah soket yang telah terhubung dengan

Page 328: Java With Eclipse

// program lain. Ambil aliran keluaran untuk melakukan sambungan,

// kirim tanggal saat ini dan tutup sambungan.

try {

System.out.println("Sambungan dari "

+ klien.getInetAddress().toString());

Date sekarang = new Date(); // Tanggal dan waktu saat ini

PrintWriter keluar; // Aliran output untuk mengirim tanggal

keluar = new PrintWriter(klien.getOutputStream());

keluar.println(sekarang.toString());

keluar.flush(); // Pastikan data telah terkirim!

klien.close();

} catch (Exception e) {

System.out.println("Kesalahan : " + e);

}

}

}

Jika kita jalankan ServerTanggal pada konsol, maka ia akan diam menunggu datangnya

permintaan sambungan dan melaporkannya apabila permintaan telah masuk. Agar layanan

ServerTanggal tetap tersedia pada suatu komputer, program tersebut seharusnya

dijalankan sebagai daemon. Daemon adalah program yang terus berjalan pada suatu

komputer, tidak peduli siapa yang menggunakan komputer itu. Komputer bisa

dikonfigurasi untuk menjalankan daemon secara otomatis ketika komputer dinyalakan.

Kemudian ia akan berjalan di latar belakang, meskipun komputer digunakan untuk hal

lainnya. Misalnya, komputer yang menyediakan layanan Web menjalankan daemon yang

mendengarkan permintaan sambungan untuk melihat halaman web dan meresponnya

dengan mengirimkan isi halaman tersebut. Bagaimana menjalankan program sebagai

daemon tidak akan kita bahas di sini, dan bisa Anda temui pada buku-buku tentang

administrasi server dan jaringan.

Lihat setelah memanggil keluar.println() untuk mengirim data ke klien, program

server memanggil keluar.flush(). Metode flush() tersedia pada semua kelas aliran

output. Metode ini digunakan untuk menjamin bahwa data yang telah dikirim pada aliran

benar-benar dikirim ke tujuannya. Kita harus memanggil fungsi ini setiap kali kita

menggunakan aliran output untuk mengirim data melalui jaringan. Jika tidak, ada

kemungkinan program akan mengumpulkan banyak data dan mengirimkan semuanya

sekaligus. Mungkin dari segi efisiensi terlihat bagus, akan tetapi tentunya pesan akan

Page 329: Java With Eclipse

sangat lambat sampai di program klien. Atau bahkan masih ada data yang belum terkirim

hingga soket ditutup.

Berikut ini adalah screen shot hasil pemanggilan program di atas pada dua konsol, masing-

masing untuk server dan kliennya.

Dan program di atas dapat diunduh pada daftar sisipan di bawah, dan diimpor ke dalam

Eclipse dengan menggunakan instruksi pada halaman berikut.

Untuk menjalankan program di atas, jalankan program server terlebih dahulu, dari dalam

konsol ketik "cd <nama_direktori_tempat_proyek_server_berada>\bin" (di screen shot di

atas direktorinya berada di c:\belajarjava.lyracc.com\servertanggal\bin) kemudian ketik

"java ServerTanggal".

Kemudian untuk menjalankan program klien, lakukan dengan cara yang serupa, yaitu buka

konsol baru, kemudian ketik "cd <nama_direktori_tempat_proyek_klien_berada>\bin" (di

screen shot di atas direktornya berada di c:\belajarjava.lyracc.com\klientanggal\bin)

kemudian ketik "java KlienTanggal localhost".

Untuk mengetahui di direktori mana proyek ini berada pada Eclpse Anda, klik kanan

proyek tersebut dari dalam Eclipse -> Properties, seperti pada screen shot berikut ini :

Page 330: Java With Eclipse

Pemrograman Serentak (Concurrency)

Java adalah bahasa pemrograman banyak thread, yang artinya beberapa hal bisa dilakukan

bersama-sama. Thread adalah unit terkecil dari eksekusi suatu program. Thread

mengeksekusi rangkaian instruksi satu demi satu. Ketika sistem menjalankan program,

komputer akan membuat thread baru. (Thread dalam konteks ini disebut proses, akan tetapi

perbedaanya tidank penting di sini). Instruksi-instruksi dalam program akan dieksekusi

oleh thread ini secara berantai, satu demi satu dari awal hingga akhir. Thread disebut

"mati" jika program selesai dieksekusi.

Dalam sistem komputer modern, beberapa thread bisa tercipta dalam satu waktu. Pada satu

saat tertentu, hanya ada satu thread yang bisa dijalankan, karena CPI hanya bisa melakukan

satu hal dalam satu waktu. (Pada komputer dengan multiprosesor, multicore, dan hyper-

threading, masing-masing prosesor atau core melakukan thread yang berbeda-beda). Akan

tetapi sebenarnya komputer membagi waktu menjadi bagian-bagian kecil sehingga seolah-

olah seluruh thread dijalankan secara bersama-sama. Pembagian waktu berarti CPU

mengeksekusi suatu thread dalam kurun waktu tertentu, setelah itu beralih mengeksekusi

thread yang lain, kemudian thread lain, dan seterusnya dan kemudian kembali ke thread

Page 331: Java With Eclipse

pertama -- kira-kira 100 kali per detik. Di mata user, semua thread berjalan pada saat yang

sama.

Java adalah bahasa pemrograman banyak thread. Artinya Java bisa membuat satu atau

lebih thread yang bisa dijalankan secara paralel. Hal ini adalah bagian mendasar, yang

dibuat di dalam core bahasa, bukan merupakan tambahan (add-on) seperti bahasa

pemrograman lain. Tetap saja pemrogaman dengan banyak thread adalah sesuatu yang

tidak mudah.

Penggunaan thread yang banyak digunakan adalah untuk membuat GUI (graphical user

interface) yang responsif. Pada dasarnya suatu program harus dapat terus bejalan dan pada

saat yang sama tetap bisa menerima input dari user, menanggapi klik mouse, dan

sebagainya.

Thread juga digunakan untuk mempercepat suatu proses, misalnya kita ingin membuat

program yang menunggu suatu input I/O dari network, dan pada saat yang sama

mengolahnya sehingga proses pengolahan berjalan serentak. Jika program harus menunggu

seluruh input datang baru kemudian melakukan pengolahan, tentunya akan memakan

waktu yang lebih lama, terutama apabila aliran network lambat atau pengolahannya

memakan waktu lama.

Jika kita memiliki CPU multiprocessor atau multicore, maka menggunakan banyak thread

akan mempercepat eksekusi program, karena masing-masing thread dijalankan secara

terpisah. Misalnya untuk melakukan video encoding dengan jumlah data besar, jika kita

menggunakan seluruh core yang tersedia maka prosesnya akan dapat diselesaikan dengan

cepat.

Dasar-dasar Thread

Cara termudah untuk membuat thread adalah membuat kelas turunan dari

java.lang.Thread, yang memiliki semua metode untuk membuat dan menjalankan

thread. Metode paling penting adalah run(), yang bisa kita beban-lebihkan untuk

melakukan tugas yang kita butuhkan. Atau dengan kata lain run() adalah metode yang

akan dijalankan bersamaan dengan thread lain.

Contoh berikut membuat 5 thread, masing-masing memiliki nomor identifikasi unik yang

dibuat dengan menggunakan variabel statik. Metode run() dibebanlebihkan untuk

menghitung mundur hingga hitungMundur bernilai nol. Setelah metode run() selesai

dijalankan, thread akan mati secara otomatis.

(Contoh-contoh pada bagian ini bisa diunduh untuk diimport ke dalam Eclipse. Lihat akhir

halaman ini untuk tautannya)

Page 332: Java With Eclipse

package com.lyracc.threaddasar1;

public class ThreadDasar extends Thread {

private int hitungMundur = 5;

private static int jumlahThread = 0;

public ThreadDasar() {

super("Thread ke-" + ++jumlahThread);

start();

}

public void run() {

while (true) {

System.out.println( getName() + " : " + hitungMundur );

if (--hitungMundur == 0)

return;

}

}

/**

* @param args

*/

public static void main(String[] args) {

for(int i = 0; i < 5; i++)

new ThreadDasar();

}

}

Pada contoh program di atas, objek thread diberi nama melalui argumen pada

konstruktornya. Nama ini dipanggil ketika metode run() melakukan penghitungan

mundur, yaitu dengan menggunakan metode getName().

Metode run() pada thread biasanya memiliki perulangan internal yang akan terus menerus

dipanggil hingga tidak lagi digunakan. Kita harus membuat suatu kondisi sehingga bisa

keluar dari perulangan tersebut (misalnya pada contoh di atas, perulangan akan selesai jika

hitungMundur bernilai 0). Seringkali, run() dijalankan di dalam perulangan yang tak

pernah berhenti (kita akan lihat nanti bagaimana menghentikan suatu thread dengan aman).

Pada metode main(), thread dibuat beberapa kali kemudian dijalankan. Metode start()

pada kelas Thread digunakan untuk melakukan tugas tertentu sebelum metode run()

dijalankan. Jadi, langkah-langkahnya adalah : konstruktor dipanggil untuk membuat objek,

Page 333: Java With Eclipse

kemudian memanggil start() untuk melakukan konfigurasi thread, dan kemudian metode

run() dijalankan. Jika kita tidak memanggil start() maka metode run() tidak akan

pernah dijalankan.

Keluaran dari program ini akan berbeda setiap kali dijalankan, karena penjadwalan thread

tidak dapat ditentukan dengan pasti (non-deterministik). Bahkan, kita bisa melihat

perbedaan yang sangat jelas ketika kita menggunakan versi JDK yang berbeda. Misalnya,

JDK lama tidak melakukan pembagian waktu lebih cepat, artinya, 1 thread mungkin bisa

melakukan tugasnya dengan cepat hingga selesai sebelum thread lain dijalankan. Pada JDK

lain kita akan melihat program akan mencetak 5 untuk seluruh thread hingga 1 untuk

seluruh thread. Artinya pembagian waktunya lebih baik, karena setiap thread memiliki

kesempatan yang sama untuk menjalankan program. Karenanya, untuk membuat suatu

program multi-threading, kita tidak boleh terpaku pada keluaran suatu kompiler. Program

kita harus dibuat seaman mungkin.

Ketika objek Thread dibuat pada metode main(), kita lihat bahwa kita tidak menyimpan

referensi ke objek tersebut. Pada objek biasa, tentunya objek ini akan langsung ditangkap

oleh pemulung memori karena objek ini tidak direferensikan di manapun. Akan tetapi pada

thread, objek hanya bisa diambil oleh pemulung memori jika metode run() selesai

dijalankan. Pada contoh di atas, program masih bisa berjalan seperti biasa, dan objek

Thread akan diberikan kepada pemulung memori setelah mencetak angka 1.

Yielding (menghasilkan)

Jika kita tahu bahwa kita telah mendapatkan hasil yang kita inginkan pada metode run(),

kita bisa memberi tahu penjadwal thread bahwa kita telah selesai dan memberi jalan

kepada thread lain untuk mendapatkan kesempatan pada CPU. Akan tetapi ini hanya

sebagai petunjuk, yang artinya belum tentu dijalankan oleh penjadwal thread.

Misalnya pada contoh di atas, kita bisa mengganti isi metode run() dengan

public void run() {

while (true) {

System.out.println( getName() + " : " + hitungMundur );

if (--hitungMundur == 0)

return;

yield();

}

}

Secara umum, yield mungkin berguna untuk situasi yang agak langka, dan kita tidak bisa

menggunakannya secara serius untuk memperbaiki kinerja aplikasi kita.

Page 334: Java With Eclipse

Tidur (sleeping)

Cara lain untuk mengatur perilaku thread kita adalah dengan memanggil sleep untuk

menunda eksekusi thread selama waktu tertentu (dalam mili detik). Misalnya pada kode

berikut, kita ubah metode run() menjadi seperti :

public void run() {

while (true) {

System.out.println( getName() + " : " + hitungMundur );

if (--hitungMundur == 0)

return;

try {

sleep(100);

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

}

}

Ketika kita memanggil sleep(), metode ini harus diletakkan di dalam blok try karena

sleep() bisa melemparkan pengecualian, yaitu jika tidurnya diganggu sebelum waktunya

selesai. Hal ini terhadi misalnya apabila thread lain yang memiliki referensi ke thread ini

memanggil interrupt() pada thread ini. Pada contoh di atas, kita lemparkan lagi

pengecualian yang terjadi dengan pengecualian lain bertipe RuntimeException, karena

kita tidak tahu bagaimana pengecualian ini harus ditangani, dan membiarkan metode yang

memanggilnya menangkap pengecualian baru ini.

Metode sleep() tidak digunakan untuk mengatur bagaimana thread akan berjalan menurut

urutan tertentu. Metode ini hanya menghentikan eksekusi suatu thread sementara. Yang

dijamin adalah bahwa thread akan tidur selama paling sedikit 100 mili detik (atau mungkin

sedikit lebih lama hingga thread jalan kembali). Urutan thread diatur oleh penjadwal thread

yang memiliki mekanisme sendiri tergantung dari keadaan thread lain atau bahkan aplikasi

lain di luar Java, oleh karena itu sifatnya disebut non-deterministik.

Jika kita harus mengatur thread mana dahulu yang harus dijalankan, cara terbaik mungkin

tidak menggunakan thread sama sekali, atau mendesain agar suatu thread memanggil

thread lain dengan suatu urutan tertentu. Tentunya cara terakhir lebih rumit dari yang

dibayangkan.

Prioritas

Prioritas suatu thread digunakan untuk memberi tahu penjadwal thread tentang prioritas

thread tersebut. Tetap saja urutannya tidak bisa ditentukan karena sifatnya yang non-

Page 335: Java With Eclipse

deterministik. Jika ada beberapa thread yang sedang diblok dan menunggu giliran untuk

dijalankan, penjadwal thread akan cenderung menjalankan thread dengan prioritas tertinggi

terlebih dahulu. Akan tetapi, tidak berarti thread dengan prioritas rendah tidak akan pernah

dijalankan, hanya lebih jarang dijalankan ketimbang thread dengan prioritas tinggi.

Perhatikan contoh berikut :

package com.lyracc.prioritasthread;

public class PrioritasThread extends Thread {

private int hitungMundur = 5;

private volatile double d = 0; // No optimization

public PrioritasThread(int prioritas) {

setPriority(prioritas);

start();

}

public void run() {

while (true) {

for(int i = 1; i < 100000; i++)

d = d + (Math.PI + Math.E) / (double)i;

System.out.println(this.toString() + " : " + hitungMundur);

if (--hitungMundur == 0)

return;

}

}

/**

* @param args

*/

public static void main(String[] args) {

new PrioritasThread(Thread.MAX_PRIORITY);

for(int i = 0; i < 5; i++)

new PrioritasThread(Thread.MIN_PRIORITY);

}

}

Pada contoh di atas, kita ubah konstruktornya untuk mengeset prioritas kemudian

menjalankan thread. Pada metode main() kita buat 6 thread, yang pertama dengan prioritas

maximum, dan yang lain dengan prioritas minimum. Perhatikan keluarannya, bagaimana

Page 336: Java With Eclipse

thread pertama dijalankan lebih dulu sedangkan thread-thread lain berjalan seperti biasa

dalam kondisi acak karena memiliki prioritas yang sama.

Di dalam metode run() kita lakukan perhitungan matematika selama 100.000 kali.

Tentunya ini perhitungan yang memakan waktu sehingga setiap thread harus menunggu

giliran di saat thread lain sedang dijalankan. Tanpa perhitungan ini, thread akan

dilaksanakan sangat cepat dan kita tidak bisa melihat efek dari prioritas thread.

Prioritas suatu thread bisa kita set kapan saja (tidak harus pada konstruktor) dengan metode

setPriority(int prioritas) dan kita bisa membaca prioritas suatu thread dengan

menggunakan metode getPriority().

Meskipun JDK memiliki 10 tingkat prioritas, akan tetapi sistem operasi memiliki tingkat

prioritas yang berbeda-beda. Windows misalnya memiliki 7 tingkat dan Solaris memiliki

231 tingkat prioritas. Yang lebih pasti adalah menggunakan konstanta MAX_PRIORITY,

NORM_PRIORITY, dan MIN_PRIORITY pada kelas thread.

Thread Daemon

Thread daemon adalah thread yang bekerja di belakang layar yang memberikan layanan

umum kepada thread-thread lain selama program berjalan, akan tetapi thread ini bukan

bagian penting dari suatu program. Artinya ketika semua thread yang bukan daemon

selesai dijalankan, program akan berhenti, dan jika masih ada thread non-daemon yang

masih dieksekusi, program tidak akan berhenti.

Perhatikan contoh program berikut ini.

package com.lyracc.threaddaemon;

public class ThreadDaemon extends Thread {

public ThreadDaemon() {

setDaemon(true); // Harus dipanggil sebelum start

start();

}

public void run() {

while (true) {

try {

sleep(100);

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

System.out.println(this);

}

Page 337: Java With Eclipse

}

/**

* @param args

*/

public static void main(String[] args) {

for (int i = 0; i < 5; i++)

new ThreadDaemon();

}

}

Perintah setDaemon() sebelum metode start() dipanggil. Pada metode run(), thread

diperintahkan untuk tidur selama 100 mili detik. Ketika semua thread dimulai, program

langsung berhenti sebelum thread bisa mencetak dirinya. Ini karena semua thread kecuali

main() adalah thread daemon. Hanya thread non-daemon saja yang bisa mencegah

program untuk terus berjalan.

Untuk mengetahui suatu thread adalah thread daemon atau bukan, kita bisa menggunakan

perintah isDaemon(). Suatu thread daemon akan membuat thread yang juga merupakan

thread daemon.

Menggabungkan thread

Perintah join() bisa digunakan pada thread lain untuk menunda eksekusi hingga thread

lain tersebut selesai dijalankan. Misalnya, jika thread a memanggil t.join() pada thread

t, maka eksekusi thread a akan terhenti sementara hingga thread t selesai dijalankan (atau

ketika t.isAlive() bernilai false).

Kita bisa juga memanggil join() dengan argumen waktu (baik dalam mili detik, ataupun

milidetik dan nanodetik), yaitu jika thread target tidak selesai dalam kurun waktu tersebut,

eksekusi pada thread induk akan kembali dilakukan.

Panggilan join() bisa dibatalkan dengan memanggil interrupt() pada thread induk,

sehingga klausa try ... catch diperlukan pada metode join().

Mari kita lihat contoh berikut ini.

package com.lyracc.joindemo;

class ThreadPemalas extends Thread {

private int waktu;

public ThreadPemalas(String namaThread, int waktuTidur) {

super(namaThread);

Page 338: Java With Eclipse

waktu = waktuTidur;

start();

}

public void run() {

try {

sleep(waktu);

} catch (InterruptedException e) {

System.out.println(getName() + " dibangunkan. "

+ "isInterrupted(): " + isInterrupted());

return;

}

System.out.println(getName() + " sudah bangun.");

}

}

class ThreadPenggabung extends Thread {

private ThreadPemalas sleeper;

public ThreadPenggabung(String namaThread, ThreadPemalas pemalas) {

super(namaThread);

this.sleeper = pemalas;

start();

}

public void run() {

try {

sleeper.join();

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

System.out.println(getName() + " selesai setelah " +

sleeper.getName());

}

}

public class JoinDemo {

/**

* @param args

*/

public static void main(String[] args) {

Page 339: Java With Eclipse

ThreadPemalas brr = new ThreadPemalas("brr", 2000);

ThreadPemalas grr = new ThreadPemalas("grr", 2000);

ThreadPenggabung saya = new ThreadPenggabung("saya",brr);

ThreadPenggabung anda = new ThreadPenggabung("anda",grr);

grr.interrupt();

}

}

Hasil keluarannya adalah seperti pada gambar berikut.

ThreadPemalas adalah thread yang akan ditidurkan sepanjang waktu yang diberikan pada

konstruktornya. Metode run() bisa berhenti jika waktu tidur sudah habis atau ada interupsi

yang terjadi. Di dalam klausa catch, interupsi akan dilaporkan. Fungsi isInterrupted()

melaporkan apakah thread ini diinterupsi atau tidak. Akan tetapi ketika thread ini

diinterupsi, kemudian pengecualiannya ditangkap oleh klausa catch, misalnya, maka tanda

interupsi akan segera dihapus. Oleh karenanya isInterrupted() akan selalu bernilai

false pada program di atas. Tanda interupsi akan digunakan pada situasi lain yang

mungkin berada di luar pengecualian.

ThreadPenggabung adalah thread yang menunggu hingga ThreadPemalas selesai dengan

tugasnya, yaitu dengan memanggil join() ke objek ThreadPemalas pada metode run()-

nya.

Pada metode utama main(), setiap ThreadPemalas tersambung pada ThreadPenggabung.

Dan kita lihat pada keluarannya, jika ThreadPemalas selesai bekerja, baik karena

dibangunkan melalui interupsi atau karena waktu sudah selesai, ThreadPenggabung yang

tersambung juga akan menyelesaikan tugasnya.

Variasi Kode

Pada contoh-contoh di atas, semua objek thread yang kita buat diturunkan dari kelas

Thread. Kita hanya membuat objek yang berfungsi sebagai thread dan tidak memiliki

tugas dan fungsi lain. Akan tetapi, kelas kita mungkin saja merupakan kelas turunan dari

kelas lain. Karena Java tidak mendukung pewarisan berganda, kita tidak bisa menurunkan

kelas tersebut bersamaan dengan kelas Thread.

Page 340: Java With Eclipse

Dalam hal ini, kita bisa menggunakan cara alternatif yaitu dengan mengimplementasi

interface Runnable. Runnable hanya memiliki satu metode untuk diimplementasi, yaitu

metode run().

Contoh berikut mendemonstrasikan contoh penggunaannya :

package com.lyracc.runnablesederhana;

public class RunnableSederhana implements Runnable {

private int hitungMundur = 5;

public void run() {

while (true) {

System.out.println(Thread.currentThread().getName() + " : " +

hitungMundur);

if (--hitungMundur == 0)

return;

}

}

public static void main(String[] args) {

for (int i = 1; i <= 5; i++) {

// Buat thread baru dan jalankan

Thread a = new Thread(new RunnableSederhana(), "Thread ke-" +

i);

a.start();

}

}

}

Satu-satunya yang dibutuhkan oleh kelas RunnableSederhana adalah metode run(), akan

tetapi jika kita ingin melakukan hal lainnya, seperti getName(), sleep(), dan lainnya, kita

harus secara eksplisit memberikan referensi dengan menggunakan

Thread.currentThread().

Ketika suatu kelas mengimplementasikan interface Runnable, artinya kelas ini memiliki

metode bernama run(), akan tetapi tidak berarti bahwa kelas ini bisa melakukan sesuatu

seperti kelas Thread atau kelas-kelas turunan yang kita buat dari kelas ini. Kita harus

membuat objek Thread sendiri seperti ditunjukkan dalam metode main() di atas,

kemudian menjalankan start() sendiri.

Kemudahan yang ditawarkan oleh interface Runnable adalah kemungkinan untuk

menggabungkannya dengan kelas dan interface lain. Misalnya kita ingin membuat kelas

Page 341: Java With Eclipse

baru yang merupakan kelas turunan dari suatu kelas lain. Kita cukup menambahkan

impement Runnable pada definisi kelasnya untuk membuat kelas yang bisa kita jadikan

thread. Dengan cara ini, kita masih bisa mengakses anggota kelas induk secara langsung,

tanpa melalui objek lain. Akan tetapi, kelas dalam (inner class) juga bisa mengakses

anggota kelas luar (outer class). Kadang-kadang kita ingin juga membuat kelas dalam yang

merupakan turunan dari kelas Thread.

Perhatikan beberapa variasi untuk mendeklarasikan dan menggunakan thread pada contoh

berikut ini.

package com.lyracc.variasithread;

// Kelas dalam bernama

class KelasDalamBernama {

private int hitungMundur = 5;

private Dalam dalam;

// Kelas Dalam adalah kelas dalam (inner class) yang

// merupakan kelas turunan kelas Thread

private class Dalam extends Thread {

Dalam(String nama) {

super(nama);

start();

}

public void run() {

while (true) {

System.out.println(getName() + " : " + hitungMundur);

if (--hitungMundur == 0)

return;

try {

sleep(10);

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

}

}

} // akhir Dalam

// Konstruktor KelasDalamBernama

// Membuat objek baru yang merupakan instansi kelas Dalam

Page 342: Java With Eclipse

public KelasDalamBernama(String nama) {

dalam = new Dalam(nama);

}

} // akhir KelasDalamBernama

// Kelas dalam anonim

class KelasDalamAnonim {

private int hitungMundur = 5;

private Thread t;

// Konstruktor KelasDalamAnonim

public KelasDalamAnonim(String nama) {

// Kelas anonim turunan Thread

t = new Thread(nama) {

public void run() {

while (true) {

System.out.println(getName() + " : " + hitungMundur);

if (--hitungMundur == 0)

return;

try {

sleep(10);

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

}

}

}; // akhir kelas anonim

t.start();

}

} // akhir KelasDalamAnonim

// Kelas dalam implementasi runnable bernama

class KelasRunnableBernama {

private int hitungMundur = 5;

private Dalam dalam;

// Kelas Dalam adalah kelas dalam (inner class) yang

// merupakan kelas yang mengimplementasi Runnable

private class Dalam implements Runnable {

Thread t;

Page 343: Java With Eclipse

Dalam(String nama) {

t = new Thread(this, nama);

t.start();

}

public void run() {

while (true) {

System.out.println(t.getName() + " : " + hitungMundur);

if (--hitungMundur == 0)

return;

try {

Thread.sleep(10);

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

}

}

} // akhir kelas Dalam

// Konstruktor KelasRunnableBernama

// Membuat objek baru yang merupakan instansi kelas Dalam

public KelasRunnableBernama(String nama) {

dalam = new Dalam(nama);

}

} // akhir KelasRunnableBernama

// Kelas dalam implementasi runnable anonim

class KelasRunnableAnonim {

private int hitungMundur = 5;

private Thread t;

public KelasRunnableAnonim(String nama) {

t = new Thread(new Runnable() {

public void run() {

while (true) {

System.out.println(Thread.currentThread().getName() +

" : " + hitungMundur);

if (--hitungMundur == 0)

return;

try {

Thread.sleep(10);

Page 344: Java With Eclipse

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

}

}

}, nama); // akhir kelas dalam anonim

t.start();

}

} // akhir KelasRunnableAnonim

// Menjalankan thread dari dalam metode dan kelas anonim

class ThreadViaMetode {

private int hitungMundur = 5;

private Thread t;

private String nama;

public ThreadViaMetode(String nama) {

this.nama = nama;

}

public void runThread() {

if (t == null) {

// Definisi kelas anonim dari dalam metode

t = new Thread(nama) {

public void run() {

while (true) {

System.out.println(getName() + " : " +

hitungMundur);

if (--hitungMundur == 0)

return;

try {

sleep(10);

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

}

}

}; // akhir kelas dalam anonim

t.start();

}

Page 345: Java With Eclipse

}

} // akhir ThreadViaMetode

public class VariasiThread {

public static void main(String[] args) {

new KelasDalamBernama("KelasDalamBernama");

new KelasDalamAnonim("KelasDalamAnonim");

new KelasRunnableBernama("KelasRunnableBernama");

new KelasRunnableAnonim("KelasRunnableAnonim");

new ThreadViaMetode("ThreadViaMetode").runThread();

}

}

Jika kita menggunakan Runnable, pada dasarnya kita menyatakan bahwa kita ingin

membuat suatu proses -- yang implementasinya berada di dalam metode run() -- bukan

suatu objek yang melakukan proses tertentu. Tentunya hal ini tergantung dari cara pandang

kita, apakah kita ingin menganggap suatu thread sebagai objek atau sesuatu yang sama

sekali berbeda, yaitu proses.

Jika kita menganggap suatu thread sebagai proses, tetntunya kita akan terbebas dari cara

pandang berorientasi objek yaitu "semuanya adalah objek". Artinya juga, kita tidak perlu

membuat seluruh kelas menjadi Runnable jika hanya kita ingin memulai proses di bagian

tertentu program kita. Karenanya, mungkin lebih masuk akal untuk menyembunyikan

thread di dalam kelas kita menggunakan kelas dalam.

KelasDalamBernama[.code] membuat kelas dalam yang merupakan kelas turunan

dari kelas Thread, dan membuat instansi kelas ini di dalam

konstruktornya. Cara ini baik jika kita ingin kelas dalam tersebut

memiliki suatu kemampuan tertentu (metode lain) yang ingin kita gunakan.

Akan tetapi, seringkali kita membuat thread hanya untuk memanfaatkan

[code]Thread saja, artinya kita mungkin tidak perlu membuat kelas yang memiliki nama.

KelasDalamAnonim adalah alternatif dari KelasDalamBernama di mana kelas dalamnya

merupakan kelas anonim yang merupakan kelas turunan dari kelas Thread. Kelas anonim

ini dibuat di dalam konstruktor dan disimpan dalam bentuk referensi t bertipe Thread. Jika

metode kelas lain membutuhkan akses ke t, maka kita bisa menggunakannya seperti

Thread biasa tanpa perlu mengetahui tipe objek t sesungguhnya.

Kelas ketiga dan keempat pada contoh di atas mirip dengan contoh pertama dan kedua,

akan tetapi menggunakan interface Runnable. Contoh ini hanya ingin menunjukkan bahwa

Page 346: Java With Eclipse

menggunakan Runnable tidak menambah nilai apa-apa, kecuali membuat kodenya lebih

sulit dibaca.

Kelas ThreadViaMetode menunjukkan bagaimana membuat thread dari dalam metode.

Kita bisa memanggil metode tersebut jika kita siap untuk menjalankan thread ini. Metode

ini akan selesai setelah thread berjalan. Jika thread hanya melakukan tugas sampingan,

mungkin cara ini lebih cocok daripada mengimplementasikan kelas khusus untuk

melakukan fungsi-fungsi thread

Berbagi Sumber Daya

Kita bisa bayangkan sebuah program dengan thread tunggal hanya memiliki satu hal yang

berpindah dari satu bagian ke bagian lain secara harmonis, karena perpindahan data dari

satu tempat ke tempat lain diatur hanya oleh satu alur. Jika ada dua atau lebih thread yang

menggunakan, membaca, menulis, menghapus data yang sama, tentunya hal ini menjadi

lebih rumit. Kita harus bisa memahami bagaimana thread-thread bekerja sama dalam

berbagi sumber daya pada komputer, termasuk memori, hard disk, tampilan, input/output

dan lain-lain, sehingga program yang kita buat menjadi lebih baik.

Ini bukan hal yang mudah, terutama karena suatu thread bersifat non-deterministik. Kita

tidak bisa menentukan atau memprediksi kapan suatu thread akan dijalankan oleh

penjadwal. Bisa saja pada saat yang bersamaan dua thread mencoba untuk mengakses data

yang sama, menghapus data yang sama, melakukan debit di rekening yang sama, mencetak

di printer yang sama, menampilkan gambar di layar yang sama. Tabrakan sumber daya

harus bisa dicegah sedini mungkin.

Cara Buruk Mengakses Sumber Daya

Kita ambil contoh berikut, di mana suatu kelas "menjamin" bahwa ia akan memberikan

angka genap setiap kali kita memanggil ambilNilai(). Akan tetapi, ada thread kedua

yang dinamakan Hansip yang selalu memanggil ambilNilai() untuk mengecek apakah

nilainya selalu genap. Sepertinya ini cara yang tidak perlu, karena setelah kita melihat kode

berikut, hasilnya pasti selalu genap. Akan tetapi, kita akan melihat ada beberapa kejutan

yang terjadi.

Berikut ini adalah program versi pertama.

package com.lyracc.selalugenap;

public class SelaluGenap {

private int i;

public void berikut() {

Page 347: Java With Eclipse

i++;

i++;

}

public int ambilNilai() {

return i;

}

public static void main(String[] args) {

final SelaluGenap genap = new SelaluGenap();

new Thread("Hansip") {

public void run() {

while (true) {

int nilai = genap.ambilNilai();

// Jika ganjil, keluar dan cetak nilainya

if (nilai % 2 != 0) {

System.out.println(nilai);

System.exit(0);

}

}

}

}.start();

while (true)

genap.berikut();

}

}

Pada metode main(), objek SelaluGenap akan dibuat -- sifatnya harus final karena

objek ini harus bisa diakses oleh kelas anonim yang berupa Thread. Jika nilai yang dibaca

oleh thread berupa bilangan ganjil, maka bilangan tersebut akan dicetak di layar kemudian

keluar dari program.

Apa yang terjadi adalah program pasti akan keluar dengan mencetak nilai ganjil. Ini berarti

ada ketidakstabilan dalam program tersebut. Ini adalah contoh masalah mendasar dengan

pemrograman banyak thread. Kita tidak pernah tahu kapan suatu thread akan jalan. Thread

kedua bisa jalan ketika thread pertama baru selesai menjalankan i++; yang pertama di

dalam metode berikut(). Di sini thread kedua menganggap ada kesalahan perhitungan,

padahal proses belum selesai.

Page 348: Java With Eclipse

Kadang-kadang kita memang tidak peduli ketika suatu sumber daya (dalam contoh di atas,

variabel i) sedang diakses apakah sedang digunakan atau tidak. Akan tetapi supaya

program banyak thread bisa bekerja dengan baik, kita harus mencegah supaya dua thread

tidak mengakses sumber daya yang sama, terutama di saat-saat kritis.

Mencegah tabrakan seperti ini bisa dicegah dengan meletakkan kunci pada sumber daya

ketika sedang digunakan. Thread pertama yang sedang mengubah variabel i seharusnya

mengunci variabel ini sehingga thread kedua yang ingin mengambil nilainya harus

menunggu hingga proses penambahan selesai.

Pemecahan Masalah Tabrakan Sumber Daya Bersama

Untuk memecahkan masalah tabrakan pada thread, hampir semua metode serentak

melakukan akses serial ke suatu sumber daya yang digunakan bersama. Artinya hanya satu

thread yang bisa mengakses suatu sumber daya pada suatu waktu. Biasanya hal ini

dilakukan dengan membuat kunci sehingga satu thread saja yang bisa mengakses kunci

tersebut. Kunci ini sering disebut mutex atau mutual exclusion.

Mari kita ambil contoh di rumah kita hanya ada satu kamar mandi. Beberapa orang

(thread) ingin masuk ke kamar mandi (sumber daya bersama), dan mereka ingin masuk

sendirian. Untuk masuk ke dalam kamar mandi, seseorang harus mengetok pintu untuk

mengetahui apakah ada orang di dalamnya. Jika tidak ada, maka mereka bisa masuk dan

mengunci pintunya. Thread lain yang mau menggunakan kamar mandi "diblok" sehingga

tidak bisa masuk, sehingga thread harus menunggu hingga seseorang keluar dari kamar

mandi.

Analogi di atas sedikit berbeda jika ketika seseorang keluar dari kamar mandi dan ada

beberapa orang yang ingin mengakses kamar mandi secara bersamaan. Karena tidak ada

"antrian" maka kita tidak tahu siapa yang harus masuk berikutnya, artinya penjadwal

thread bersifat non-deterministik. Yang terjadi adalah, jika banyak orang menunggu di

depan kamar mandi, maka siapa yang paling dekat dengan kamar mandi akan masuk

terlebih dahulu. Seperti telah diulas sebelumnya, kita bisa memberi tahu penjadwal thread

dengan perintah yield dan setPriority() akan tetapi tetap saja masih sangat bergantung

kepada JVM dan implementasi pada suatu platform dan tidak bisa ditentukan dengan pasti

siapa yang berhak masuk terlebih dahulu.

Java memiliki fitur untuk mencegah terjadinya tabrakan sumber daya, yaitu dengan

menggunakan kata kunci synchronized. Ketika suatu thread berusaha untuk

mengeksekusi suatu perintah yang diberi kata kunci synchronized, Java akan mengecek

apakah sumber daya tersebut tersedia. Jika ya, maka kunci ke sumber daya tersebut akan

Page 349: Java With Eclipse

diambil, kemudian perintah dijalankan, dan setelah selesai melepaskannya kembali. Akan

tetapi synchronized tidak selalu berhasil.

Sumber daya bersama bisa berbentuk lokasi memori (dalam bentuk objek), atau bisa juga

berupa file, I/O atau bahkan printer. Untuk mengontrol akses ke sumber daya bersama, kita

biasanya membungkusnya dalam bentuk objek. Metode lain yang mencoba untuk

mengakses sumber daya tersebut bisa diberi kata kunci synchronized. Artinya jika thread

sedang mengeksekusi salah satu metode synchronized, thread lain diblok untuk

mengeksekusi metode synchronized lain dalam kelas itu hingga thread pertama selesai.

Karena biasanya data dari suatu kelas kita buat private dan akses ke memori hanya bisa

dilakukan dengan menggunakan metode, maka kita bisa mencegah tabrakan dengan

membuat metode menjadi synchronized. Berikut ini adalah contoh pendeklarasian

synchronized.

synchronized void a() { /* perintah Anda di sini */ }

synchronized void b() { /* perintah Anda di sini */ }

Setiap objek memiliki kunci masing-masing yang otomatis dibuat ketka objek tersebut

dibuat (kita tidak perlu membuat kode spesial). Ketika kita memanggil metode yang diberi

tanda synchronized, objek tersebut dikunci dan tidak boleh ada lagi metode

synchronized yang bisa dieksekusi hingga metode sebelumnya selesai dijalankan dan

kunci dilepas. Karena hanya ada satu kunci untuk setiap objek, maka kita tidak mungkin

menyimpan 2 data pada satu tempat pada saat yang bersamaan.

Satu thread bisa mengunci objek beberapa kali. Ini terjadi jika satu metode memanggil

metode lain di kelas yang sama, kemudian metode tersebut memanggil metode lain lagi di

kelas yang sama dan seterusnya. JVM akan melacak berapa kali objek tersebut terkunci.

Setiap kali suatu metode selesai, kunci akan dilepas. Ketika objek tidak terkunci lagi, maka

kuncinya bernilai 0, yang artinya thread lain bisa mulai menggunakan metode pada objek

ini.

Ada juga kunci per kelas, yang artinya kunci ini berlaku untuk suatu kelas. Otomatis semua

objek yang diciptakan dari kelas yang sama memiliki kunci bersama. Caranya yaitu dengan

menggunakan synchronized static metode sehingga suatu objek bisa juga mengunci

kelas sehingga objek lain yang menggunakan metode ini tidak bisa jalan apabila sedang

digunakan oleh objek lain.

Memperbaiki SelaluGenap

Kita akan ubah sedikit program SelaluGenap di awal bagian ini untuk memberikan kata

kunci synchronized pada metode berikut() dan ambilNilai(). Jika kita hanya

Page 350: Java With Eclipse

meletakkan kunci pada salah satu metode, maka metode yang tidak diberi kunci akan tetap

bebas untuk dieksekusi mengabaikan ada atau tidaknya kunci. Di sini lah kunci

pemrograman serentak, di mana kita harus memberi kunci di setiap akses ke sumber daya

bersama.

Metode ini akan berjalan terus menerus, oleh karena itu kita akan gunakan waktuMulai

untuk menyimpan waktu ketika thread mulai berjalan, kemudian secara periodik mengecek

waktu saat ini. Jika proses sudah berjalan lebih dari 4 detik, kita hentikan proses kemudian

mencetak hasilnya.

package com.lyracc.selalugenapsynchronized;

public class SelaluGenapSynchronized {

private int i;

synchronized public void berikut() {

i++;

i++;

}

synchronized public int ambilNilai() {

return i;

}

public static void main(String[] args) {

final SelaluGenapSynchronized genap = new

SelaluGenapSynchronized();

new Thread("Hansip") {

// mencatat waktu ketika thread dimulai

private long waktuMulai = System.currentTimeMillis();

public void run() {

while (true) {

int nilai = genap.ambilNilai();

// Jika ganjil, keluar dan cetak nilainya

if (nilai % 2 != 0) {

System.out.println(nilai);

System.exit(0);

}

// Selesaikan program jika sudah melewati 4 detik

if (System.currentTimeMillis() - waktuMulai > 4000) {

Page 351: Java With Eclipse

System.out.println(nilai);

System.exit(0);

}

}

}

}.start();

while (true)

genap.berikut();

}

}

Bagian Kritis

Kadang-kadang kita hanya ingin mencegah beberapa thread untuk mengakses sebagian

kode saja di dalam suatu metode, bukan keseluruhan metode. Bagian kode yang kita ingin

lindungi ini disebut bagian kritis (critical section) dan juga bisa dibuat dengan kata kunci

synchronized. Akan tetapi, kata kunci ini digunakan dengan menyatakan objek mana

yang memiliki kunci yang harus dicek sebelum bagian ini dijalankan.

Berikut ini adalah bentuk umum dari pernyataan synchronized untuk melindung bagian

kritis :

synchronized(objekKunci) {

// Kode di bagian ini hanya bisa diakses

// Jika objekKunci sedang tidak diakses oleh thread lain

}

Bentuk umum di atas juga disebut blok tersinkron (synchronized block); sebelum blok ini

bisa dieksekusi, kunci pada objek objekKunci harus dicek terlebih dahulu. Jika thread lain

telah mengunci ojek ini, maka bagian kritis tidak bisa dimasuki hingga thread lain selesai

dan melepas kuncinya.

Siklus Hidup Thread

Suatu thread bisa berada dalam salah satu kondisi berikut :

1. Baru : Objek thread baru saja dibuat, akan tetapi belum mulai dijalankan, sehingga

belum bisa berbuat apa-apa.

2. Bisa-jalan : Artinya objek ini sudah dimulai dan sudah bisa dijalankan oleh mekanisme

pembagian waktu oleh CPU. Sehingga thread ini bisa jalan kapan saja, selama

diperintahkan oleh penjadwal thread.

3. Mati : suatu thread biasanya mati ketika selesai menjalankan metode run().

Sebelumnya, kita bisa memanggi metode stop(), akan tetapi program bisa berada dalam

Page 352: Java With Eclipse

kondisi tidak stabil jika metode ini dipanggil. Kita akan lihat beberapa metode lain untuk

menghentikan thread di bagian berikutnya.

4. Diblok : Thread seharusnya bisa berjalan, akan tetapi ada yang menghalanginya. Salah

satunya adalah jika thread menunggu di bagian kritis sementara ada thread lain yang

sedang menjalankan bagian kritis tersebut. Ketika suatu thread berada dalam kondisi

diblok, penjadwal thread akan mengabaikannya dan tidak memberikan waktu CPU.

Bagaimana Suatu Thread Berada dalam Kondisi Diblok

Ketika suatu thread diblok, ada suatu alasan kenapa thread tersebut tidak bisa terus

berjalan. Suatu thread dapat diblok karena beberapa alasan sebagai berikut :

Kita memberi perintah thread untuk tidur dengan sleep(milidetik) sehingga

thread tidak akan jalan dalam waktu yang sudah disebutkan

Kita memerintahkan thread untuk menunggu dengan perintah wait(). Thread tidak

akan dijalankan kembali hingga diberikan pesan notify() atau notifyAll().

Thread sedang menunggu selesainya operasi I/O

Thread mencoba memanggil metode dengan kata kunci synchronized, akan tetapi

thread lain sedang memegang kuncinya.

Kerjasama Antar Thread

Setelah kita mengerti bagaimana thread bisa bertabrakan satu sama lain, dan bagaimana

caranya mencegah tabrakan antar thread, langkah berikutnya adalah belajar bagaimana

membuat thread dapat bekerja sama satu sama lain. Kuncinya adalah komunikias antar

thread yang diimplementasi dengan aman dalam metode-metode pada kelas Object, yaitu

wait() dan notify().

wait() dan notify()

Pertama-tama penting untuk mengerti bahwa sleep() tidak melepas kunci thread ketika

dipanggil. Artinya jika sleep() dipanggil dari dalam bagian kritis, maka thread lain tidak

bisa masuk hingga thread yang memanggil sleep() bangun, meneruskan eksekusi, hingga

keluar dari bagian kritis. Sedangkan wait() melepas kunci ketika dipanggil, sehingga

thread lain bisa masuk ke dalam bagian kritis.

Ada dua bentuk wait(). Yang pertama memiliki argumen waktu dalam bentuk mili detik

(mirip dengan sleep(). Perbedaannya dengan sleep() adalah :

wait() melepaskan kunci

Kita bisa membatalkan wait() dengan menggunakan notify() atau

notifyAll(), atau hingga waktu tunggu berlalu.

Page 353: Java With Eclipse

Bentuk kedua dari wait() adalah wait() yang tidak memiliki argumen. Jenis wait() ini

akan terus berlangsung hingga dibatalkan dengan notify atau notifyAll().

Aspek penting dari wait(), notify() dan notifyAll() adalah metode ini merupakan

bagian dari kelas dasar Obejct dan bukan bagian dari kelas Thread seperti sleep().

Meskipun kelihatan janggal, hal ini sangat penting karena semua objek memiliki kunci.

Artinya kita bisa memanggil wait() dari dalam metode synchronized, tidak peduli

apakah kelas tersebut merupakan kelas turunan dari Thread atau bukan.

Sebetulnya satu-satunya tempat kita bisa memanggil wait(), notify() dan notifyAll()

adalah dari dalam blok atau metode synchronized. (sleep() bisa dipanggil dari manapun

karena ia tidak berhubungan dengan kunci suatu objek). Jika kita memanggil wait(),

notify() atau notifyAll() dari luar metode atau blok synchronized, compiler tidak

akan memperingatkan Anda, akan tetapi ketika program dijalankan, kita akan mendapatkan

pengecualian IllegalMonitorStateException dengan pesan kesalahan yang tidak

dimengerti, seprti "thread ini bukan pemiliknya". Pesan ini berarti bahwa thread yang

memanggil wait(), notify() atau notifyAll() harus memiliki kunci objek sebelum bisa

memanggil salah satu metode ini.

Kita juga bisa meminta suatu objek untuk memanipulasi kuncinya sendiri. Caranya,

pertama-tama kita harus mengambil kuncinya. Misalnya, jika kita ingin memanggil

notify() ke suatu objek x, kita harus melakukannya di dalam blok synchronized untuk

mengambil kunci x, seperti :

synchronized(x) {

x.notify();

}

Biasanya, wait() digunakan jika kita menunggu sesuatu yang dikontrol oleh sesuatu di

luar kontrol metode kita (di mana sesuatu ini hanya bisa diubah oleh thread lain). Kita

tidak ingin menunggu dan berulang-ulang menguji apakah sesuatu itu sudah tersedia,

karena cara ini akan memboroskan penggunaan CPU. Kita bisa menggunakan wait()

untuk memerintahkan suatu thread untuk menunggu hingga sesuatu tersebut berubah, dan

hanya ketika notify() dipanggil, maka thread tersebut akan bangun dan mengeceknya.

Dengan kata lain wait() digunakan melakukan aktifitas tak-sinkron antara beberapa

thread.

Sebagai contoh, anggap suatu restoran memiliki satu orang koki dan satu orang pelayan.

Pelayan harus menunggu hingga si koki selesai memasak makanan. Ketika koki selesai, ia

akan memberi tahu pelayan, kemudian membawa makanan ini ke customer, kemudian

Page 354: Java With Eclipse

menunggu kembali. Koki di sini kita sebut sebagai produsen, dan pelayan disebut sebagai

konsumen.

package com.lyracc.rumahmakan;

class Pesanan {

private int i = 0;

public Pesanan(int i) {

this.i = i;

}

public String toString() {

return "pesanan " + i;

}

} // akhir kelas Pesanan

class Pelayan extends Thread {

private RumahMakan rumahMakan;

public Pelayan(RumahMakan r) {

rumahMakan = r;

start();

}

public void run() {

while (true) {

while (rumahMakan.pesanan == null)

// tunggu hingga dipanggil dengan notify oleh Koki

synchronized (this) {

try {

wait();

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

}

System.out.println("Pelayan mengantarkan " +

rumahMakan.pesanan);

// pesanan sudah diantar, pesanan sekarang kosong

rumahMakan.pesanan = null;

}

}

Page 355: Java With Eclipse

} // akhir kelas Pelayan

class Koki extends Thread {

private RumahMakan rumahMakan;

private Pelayan pelayan;

public Koki(RumahMakan r, Pelayan p) {

rumahMakan = r;

pelayan = p;

start();

}

public void run() {

// masak 10 makanan

for (int i = 0; i < 10; i++) {

if (rumahMakan.pesanan == null) {

rumahMakan.pesanan = new Pesanan(i);

System.out.print("Pesanan selesai! ");

// coba panggil pelayan jika tidak sibuk

synchronized (pelayan) {

pelayan.notify();

}

}

try {

sleep(100);

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

}

System.out.println("Makanan habis..");

System.exit(0);

}

} // akhir kelas Koki

public class RumahMakan {

Pesanan pesanan;

public static void main(String[] args) {

RumahMakan rumahMakan = new RumahMakan();

Pelayan pelayan = new Pelayan(rumahMakan);

new Koki(rumahMakan, pelayan);

Page 356: Java With Eclipse

}

}

Keluarannya ada sebagai berikut.

<img src="/sites/java.lyracc.com/files/kerjasamathread_gbr1.png" alt=""

/>

Pesanan adalah kelas sederhana yang berisi pesanan. Konstruktor menerima angka yang

diibaratkan seperti pesanan, kemudian membebanlebihkan metode toString() untuk

mencetak objek ini langsung dengan System.out.println().

Seorang Pelayan harus tahu RumahMakan tempat ia bekerja, karena ia harus ke sana untuk

mengantarkan pesanan dari "jendela pemesanan", yaitu rumahMakan.pesanan. Pada

metode run(), Pelayan masuk dalam mode menunggu. Kuncinya dimiliki oleh pelayan

ini sendiri. Kunci ini yang akan digunakan oleh Koki untuk membangunkan Pelayan jika

makanan sudah siap dengan metode notify().

Pada aplikasi yang lebih kompleks, misalnya jika pelayannya banyak, kita bisa memanggil

notifyAll() untuk membangunkan semua pelayan. Setiap pelayan nanti akan menguji

apakah panggilan itu untuknya atau tidak.

Perhatikan bahwa wait() ditulis di dalam pernyataan while untuk menguji apakah

pesanan sudah datang. Mungkin ini agak terasa ganjil karena ketika thread ini dibangunkan

ketika menunggu pesanan, seharusnya pesanannya sudah tersedia khan? Masalahnya jika

aplikasinya terdiri dari banyak pelayan, thread lain mungkin sudah keburu mengantarkan

pesanannya ketika thread ini sedang bangun. Untuk itu, lebih aman apabila kita

menggunakan bentuk berikut untuk semua aplikasi yang menggunakan wait() :

while (sesuatuYangDitunggu)

wait();

Dengan melakukan ini, kita menjamin bahwa kondisi di atas akan terpenuhi sebelum

thread mendapatkan sesuatu yang ditunggu. Jika thread sudah dibangunkan akan tetapi

pesanan tidak ada, maka thread akan kembali tidur.

Objek Koki harus tahu di rumah makan mana ia bekerja. Pesanan yang dia masak akan dia

letakkan pada jendela pesanan (dalam hal ini rumahMakan.pesanan) dan dia juga harus

tahu siapa Pelayan yang akan mengantarkan pesanan yang sudah selesai dimasak.

Pada contoh sederhana di atas, Koki membuat objek Pesanan, kemudian setelah selesai

akan memanggil Pelayan dengan notify(). Karena panggilan notify() dilakukan di

dalam klausa synchronized, maka sudah bisa dipastikan Koki memanggil pelayan jika

pelayan tersebut sedang tidak digunakan oleh thread lain.

Page 357: Java With Eclipse

Kunci Mati (Deadlock)

Thread bisa diblok dan objek bisa memanggil metode synchronized ke suatu objek

sehingga objek lain tidak bisa mengakses objek tersebut hingga kuncinya dilepas.

Karenanya mungkin saja satu thread tersangkut menunggu suatu thread, akan tetapi thread

yang ditunggu ini juga sedang menunggu thread lain, dan seterusnya. Jika rangkaian kunci

kembali ke thread pertama, maka semua thread akan diam menunggu satu sama lain dan

tidak akan pernah jalan. Kasus ini dinamakan kunci mati (deadlock).

Jika program yang kita buat tiba-tiba mengalamai deadlock, kita akan segera tahu dan

memperbaikinya. Akan tetapi permasalahan utamanya adalah deadlock sulit untuk

dideteksi. Sering kali program yang kita buat tampak baik-baik saja, akan tetapi mungkin

menyimpan bahaya laten deadlock, yang suatu saat nanti terjadi ketika program sudah

dirilis (bahkan sering kali deadlock ini juga tidak bisa direproduksi sehingga menyulitkan

debugging). Mencegah deadlock dengan membuat desain program yang lebih hati-hati

sangat penting ketika kita membuat program banyak thread.

Mari kita lihat contoh klasik dari deadlock yang ditemukan oleh Dijkstra, yaitu "ilmuwan

yang sedang makan". Misalnya ada 5 orang ilmuwan (kita bisa mengganti berapa saja).

Ilmuwan-ilmuwan ini menghabiskan sebagian waktu untuk berfikir dan sebagian lagi

untuk makan. Ketika mereka berfikir, mereka tidak membutuhkan apa-apa, akan tetapi

ketika mereka makan, mereka duduk di meja dengan jumlah alat makan yang terbatas.

Mereka membutuhkan dua garpu untuk mengambil spaghetti dari mangkok di tengah meja.

Kesulitannya adalah karena ilmuwan tidak punya uang, mereka tidak mampu untuk

membeli banyak garpu. Hanya ada 5 garpu yang tersedia. Garpu-garpu ini diletakkan di

meja tersebar di dekat masing-masing ilmuwan ini. Ketika ilmuwan ingin makan, dia harus

mengambil garpu di sebelah kiri dan kanannya. Jika ilmuwan di sebelahnya sedang

menggunakan garpu tersebut, maka ia harus menunggu hingga garpunya selesai digunakan.

Persoalan ini menjadi menarik karena menjelaskan bahwa program yang sepertinya

berjalan dengan benar akan tetapi mudah terkena deadlock. Kita bisa mengganti beberapa

konstanta sehingga deadlock bisa lebih cepat terjadi, atau bisa dicegah sama sekali.

Parameter-parameter yang bisa diganti adalah konstanta bertipe final static int di

awal deklarasi kelas IlmuwanMakan. Jika kita menggunakan banyak ilmuwan dan waktu

berfikir yang lama, deadlock akan lebih jarang terjadi.

package com.lyracc.ilmuwanmakan;

import java.util.*;

Page 358: Java With Eclipse

class Garpu {

private static int hitung = 0;

private int nomor = hitung++;

public String toString() {

return "garpu " + nomor;

}

} // akhir kelas Garpu

class Ilmuwan extends Thread {

private static Random acak = new Random();

private static int hitung = 0;

private int nomor = hitung++;

private Garpu garpuKiri;

private Garpu garpuKanan;

static int waktuFikirMaks = IlmuwanMakan.WAKTU_FIKIR_MAKS;

public Ilmuwan(Garpu kiri, Garpu kanan) {

garpuKiri = kiri;

garpuKanan = kanan;

start();

}

// Ilmuwan berfikir, gunakan sleep untuk mensimulasi

public void berfikir() {

System.out.println(this + " berfikir");

try {

sleep(acak.nextInt(waktuFikirMaks));

} catch (InterruptedException e) {

throw new RuntimeException(e);

}

}

// Ilmuwan makan

public void makan() {

// cek apakah garpu kirinya tersedia

synchronized (garpuKiri) {

System.out.println(this + " punya " + this.garpuKiri

+ ". Menunggu " + this.garpuKanan);

Page 359: Java With Eclipse

// kemudian cek apakah garpu kanannya tersedia

synchronized (garpuKanan) {

System.out.println(this + " makan");

}

}

}

public String toString() {

return "Ilmuwan " + nomor;

}

// Metode ketika thread dijalankan

// masing-masing ilmuwan akan berfikir kemudian makan

// begitu seterusnya

public void run() {

while (true) {

berfikir();

makan();

}

}

} // akhir kelas ilmuwan

// Kelas timeout untuk menghentikan proses setelah

// waktu yang ditentukan

class Timeout extends Timer {

public Timeout(int jeda, final String pesan) {

super(true); // Daemon thread

schedule(new TimerTask() {

public void run() {

System.out.println(pesan);

System.exit(0);

}

}, jeda);

}

} // akhir kelas Timeout

// Kelas utama

public class IlmuwanMakan {

final static int JUMLAH_ILMUWAN = 3; // bisa diganti

final static int WAKTU_FIKIR_MAKS = 10; // mili detik, bisa diganti

Page 360: Java With Eclipse

final static boolean DEADLOCK = true; // ubah ini menjadi false

untuk mencegah deadlock

final static int WAKTU_TIMEOUT = 10000; // mili detik atau buat 0

jika tidak ingin timeout

public static void main(String[] args) {

// Buat array ilmuwan sejumlah JUMLAH_ILMUWAN

Ilmuwan[] ilmuwan = new Ilmuwan[JUMLAH_ILMUWAN];

// Mula-mula buat 2 garpu

Garpu kiri = new Garpu();

Garpu kanan = new Garpu();

// Garpu pertama hanya sebagai penanda

// yaitu garpu di kiri ilmuwan pertama

Garpu pertama = kiri;

int i = 0;

// buat masing-masing ilmuwan

// yang pertama memiliki garpu kiri dan kanan

// ilmuwan kedua duduk di sebelah kanan ilmuwan pertama

// sehingga garpu kirinya adalah garpu kanan ilmuwan pertama

// buat garpu baru untuk garpu kanannya

// demikian seterusnya hingga JUMLAH_ILMUWAN minus 1

while (i < ilmuwan.length - 1) {

ilmuwan[i++] = new Ilmuwan(kiri, kanan);

kiri = kanan;

kanan = new Garpu();

}

// Sekarang buat ilmuwan terakhir

// Jika kita ingin membuat deadlock (makan menghadap meja) :

// - garpu kirinya adalah garpu kanan ilmuwan sebelumnya

// - garpu kanannya adalah garpu kiri ilmuwan pertama

//

// Jika tidak (makan berbalik arah)

// - garpu kirinya adalah garpu kiri ilmuwan pertama

// - garpu kanannya adalah garpu kanan ilmuwan sebelumnya

if (DEADLOCK)

ilmuwan[i] = new Ilmuwan(kiri, pertama);

Page 361: Java With Eclipse

else

ilmuwan[i] = new Ilmuwan(pertama, kiri);

// Keluar dari program setelah jeda waktu selesai

if (WAKTU_TIMEOUT > 0)

new Timeout(WAKTU_TIMEOUT, "Waktu habis..");

}

}

Kelas Garpu dan Ilmuwan menggunakan penghitung otomatis untuk memberi nomor

identifikasi tersendiri untuk setiap objek Garpu dan Ilmuwan yang diciptakan. Setiap

Ilmuwan diberi referensi ke garpu kiri dan garpu kanan. Garpu-garpu ini akan diambil oleh

ilmuwan ketika hendak makan.

Variabel statik waktuFikirMaks adalah waktu maksimum yang digunakan oleh ilmuwan

untuk berfikir. Jika nilainya tidak nol, maka nilai variabel ini akan digunakan sebagai

argumen perintah sleep() dalam kelas Ilmuwan. Mungkin kita beranggapan dengan

mengubah waktu berfikir setiap ilmuwan, mereka tidak akan makan secara bersamaan

sehingga kemungkinan terjadinya deadlock menjadi lebih kecil. Padahal sebenarnya tidak

demikian.

Di dalam metode makan(), seorang ilmuwan akan mengambil garpu dengan melakukan

sinkronisasi pada garpu tersebut. Jika garpu sedang digunakan oleh ilmuwan lain, maka

ilmuwan tersebut akan menunggu hingga garpu selesai digunakan. Mula-mula garpu kiri

dahulu yang dicoba untuk diambil, baru kemudian garpu kanan. Setelah digunakan, garpu

kanan akan dilepas terlebih dahulu baru kemudian garpu kiri.

Dalam metode run() serorang ilmuwan makan dan berfikir terus menerus.

Ada empat konstanta yang bisa kita ubah-ubah di dalam kelas IlmuwanMakan.

JUMLAH_ILMUWAN dan WAKTU_FIKIR_MAKS adalah banyaknya ilmuwan yang ada dan waktu

fikir ilmuwan seperti dijelaskan sebelumnya. Variabel ketiga DEADLOCK berupa boolean

yang bisa berisi true atau false. Jika bernilai true maka program cepat atua lambat pasti

akan mengalami deadlock. Untuk menghindari deadlock, kita bisa menggantinya dengan

false. Variabel keempat, yaitu WAKTU_TIMEOUT digunakan untuk menghentikan semua

proses pada waktu tertentu. Pada proses yang sulit atau tidak mungkin deadlock (jika

variabel DEADLOCK false, atau jumlah ilmuwan banyak, atau waktu fikir ilmuwan sangat

panjang), maka proses akan berhenti pada waktu time out, seringkali sebelum deadlock

terjadi.

Page 362: Java With Eclipse

Setelah array objek Ilmuwan dibuat, dua objek Garpu akan dibuat. Objek pertama, juga

disimpan dalam variabel pertama akan digunakan kemudian. Setiap objek ilmuwan akan

diberi garpu kiri dan kanannya, kecuali objek ilmuwan terakhir. Setiap kali, garpu kiri

dipindah ke garpu kanan. Bayangkan meja ilmuwan dibuat dalam urutan melingkar

berlawanan arah jarum jam. Garpu kiri ilmuwan baru adalah garpu kanan ilmuwan

sebelumnya. Sedangkan garpu kanan ilmuwan baru adalah objek garpu baru.

Pada versi di mana DEADLOCK bernilai true, garpu kiri ilmuwan terakhir adalah garpu

kanan ilmuwan sebelumnya, akan tetapi garpu kanannya adalah garpu pertama, karena

semua ilmuwan duduk pada posisi melingkar. Dengan pengaturan seperti ini, mungkin saja

pada suatu waktu semua ilmuwan akan makan dan saling menunggu garpu di sebelahnya,

dan ilmuwan sebelahnya menunggu garpu sebelahnya lagi. Dan karena posisi duduknya

melingkar, semua saling menunggu satu sama lain.

Coba ganti variabelnya dengan beberapa nilai dan amati seberapa cepat deadlock terjadi.

Deadlock ditandai dengan semua ilmuwan saling menunggu satu sama lain hingga waktu

time out berakhir. (Seperti pada gambar berikut).

Untuk memecahkan masalah ini, kita harus mengerti bahwa deadlock bisa terjadi jika

keempat kondisi berikut ini terjadi pada saat yang sama :

1. Saling melarang (mutual exclusion): Paling sedikit salah satu sumber daya yang

digunakan objek tidak boleh digunakan bersama. Dalam hal ini, satu garpu bisa

digunakan oleh dua orang ilmuwan

2. Paling sedikit salah satu proses sedang memegang suatu sumber daya, dan di saat

yang sama menunggu sumber daya lain yang dipegang oleh proses lain. Dalam hal

ini, agar deadlock terjadi, seorang ilmuwan pasti sedang memegang satu garpu dan

menunggu garpu lain yang dipegang oleh ilmuwan lain.

3. Suatu sumber daya tidak bisa diambil secara paksa. Proses hanya bisa melepas

sumber daya dalam kondisi normal. Ilmuwan-ilmuwan kita adalah orang yang

Page 363: Java With Eclipse

beradab, sehingga tidak bisa merebut garpu yang sedang dipegang oleh ilmuwan

lain.

4. Lingkaran menunggu sedang terjadi, di mana proses pertama sedang menunggu

satu sumber daya yang dipegang oleh proses kedua, yang juga sedang menunggu

sumber daya yang dipegang oleh proses ketiga, dan seterusnya hingga proses

terakhir menunggu sumber daya yang dipegang oleh proses pertama, sehingga

semua proses saling menunggu satu sama lain. Pada contoh ini, lingkaran

menunggu terjadi karena semua ilmuwan mengambil garpu kiri terlebih dahulu

baru kemudian garpu kanan. Kita bisa memecahkan deadlock dengan membalik

garpu kiri dan garpu kanan pada ilmuwan terakhir, sehingga ilmuwan terakhir akan

mengambil garpu kanan terlebih dahulu, baru kemudian garpu kiri.

Karena semua kondisi di atas harus terjadi secara bersama-sama agar deadlock bisa terjadi,

maka untuk mencegah terjadinya deadlock, kita harus memecah salah satu kondisi saja.

Pada program ini, cara termudah adalah dengan memecah kondisi keempat. Akan tetapi ini

bukan satu-satunya pemecahan, kita bisa memecahkannya dengan teknik yang lebih

canggih. Untuk ini saya mereferensikan Anda pada buku-buku teknik threading tingkat

lanjut untuk lebih detailnya.

Kesimpulannya, Java tidak menyediakan bantuan secara alami untuk mencegah deadlock:

Anda harus menghindarinya sendiri dengan membuat program multi threading dengan

lebih hati-hati.

Menghentikan Thread

Salah satu perubahan pada Java 2 untuk mengurangi kemungkinan terjadinya deadlock

adalah dengan dideprekasi (artinya pengembangannya dihentikan, dan user disarankan

untuk menghindari penggunaannya) metode stop(), suspend(), dan resume() pada kelas

Thread.

Alasan mengapa metode stop() dideprekasi adalah karena metode ini tidak melepas kunci

yang sudah dimilikinya, dan jika objek tersebut berada dalam kondisi "cacat" seperti ini,

thread lain bisa melihat dan mengubah objek cacat ini. Hasilnya akan muncul masalah

yang tersembunyi yang akan sangat sulit dideteksi.

Java menyediakan cara lain untuk menghentikan thread, yaitu dengan mengeset suatu

variabel untuk memberi tahu thread tersebut agar menghentikan dirinya sendiri yaitu

dengan keluar dari metode run()-nya. Variabel ini akan dicek pada metode run() yang

jika bernilai true, maka metode run() akan berhenti. Berikut ini adalah contohnya :

package com.lyracc.hentikanthread;

Page 364: Java With Eclipse

import java.util.*;

class Berhenti extends Thread {

// Harus bertipe volatile:

private volatile boolean stop = false;

private int hitung = 0;

public void run() {

// Jika stop masih bernilai false teruskan cetak angka

// Jika stop bernilai true, blok ini tidak lagi dijalankan

while (!stop && hitung < 10000) {

System.out.println(hitung++);

}

// Jika stop berubah menjadi true

if (stop)

System.out.println("Permintaan stop dideteksi");

}

public void requestStop() {

stop = true;

}

}

public class HentikanThread {

/**

* @param args

*/

public static void main(String[] args) {

final Berhenti threadBaru = new Berhenti();

threadBaru.start();

new Timer(true).schedule(new TimerTask() {

public void run() {

System.out.println("Permintaan berhenti");

threadBaru.requestStop();

}

}, 500); // run() setelah 500 mili detik

}

}

Page 365: Java With Eclipse

Variabel stop harus bertipe volatile sehingga metode run() pasti bisa melihat variabel

ini (jika tidak, maka nilainya bisa saja di-cache). Tugas thread ini adalah mencetak 10000

angka, yang akan berhenti ketika hitung >= 10000 atau objek lain meminta berhenti

dengan memanggil requestStop(). Perhatikan bahwa requestStop() tidak

synchronized karena stop bertipe boolean dan volatile (mengubah boolean menjadi

[code]true adalah operasi atomis yang tidak bisa dihentikan di tengah jalan, karena

dilakukan dalam 1 clock).

Pada main(), objek Berhenti dimulai. Pada saat yang sama, Timer dimulai untuk

memanggil requestStop() setelah setengah detik (500 mili detik). Konstruktor Timer

diisi true untuk memastikan bahwa program berhenti saat itu juga.

Menginterupsi Thread yang Diblok

Kadang-kadang, ketika thread dalam keadaan diblok (misalnya ketika sedang menunggu

input), thread tersebut tidak bisa membaca variabel seperti kita lakukan di atas. Di sini, kita

bisa menggunakan metode interrupt() pada kelas Thread untuk mengeluarkannya dari

kondisi diblok. Misalnya,

package com.lyracc.interupsi;

import java.util.*;

class ThreadDiblok extends Thread {

public ThreadDiblok() {

System.out.println("Memulai blokade");

start();

}

public void run() {

try {

synchronized (this) {

wait(); // Memblok selamanya

}

} catch (InterruptedException e) {

System.out.println("Diinterupsi");

}

System.out.println("Keluar dari run()");

}

}

public class Interupsi {

Page 366: Java With Eclipse

static ThreadDiblok threadDiBlok = new ThreadDiblok();

/**

* @param args

*/

public static void main(String[] args) {

new Timer(true).schedule(new TimerTask() {

public void run() {

System.out.println("Bersiap-siap untuk interupsi");

threadDiBlok.interrupt();

threadDiBlok = null; // buat null untuk diambil oleh

pemulung memori

}

}, 2000); // run() setelah 2 detik

}

}

Panggilan wait() di dalam ThreadDiBlok.run() akan memblok thread selamanya.

Ketika Timer selesai, objek akan melakukan interupsi dengan memanggil interrupt().

Kemudian objek threadDiBlok diset ke null sehingga bisa diambil oleh pemulung

memori untuk dibersihkan.

Page 367: Java With Eclipse

Memulai SWT

Untuk memulai pemrograman dengan SWT, mari kita buat program sederhana yang kita

namakan HelloSWT.

Anda membutuhkan "link" ke pustaka SWT. Ada dua cara untuk melakukannya :

1. Menggunakan pustaka SWT bawaan dari Eclipse. Untuk ini Anda tidak perlu

mendownload apa-apa. Pustaka SWT bawaan eclipse terdapat pada direktori

Eclipse Anda, misalnya pada Windows

(C:\eclipse\plugins\org.eclipse.swt.win32.win32.x86_3.4.1.v3449c.jar) atau pada

Linux (eclise/plugins/org.eclipse.swt.gtk.linux.x86_3.4.1.v3452b.jar)

2. Menggunakan pustaka SWT beserta sumber kodenya untuk dokumentasi lebih

lengkap. Pustaka SWT ini bisa diunduh pada alamat berikut :

http://www.eclipse.org/swt/. Pilih Releases -> Stable -> (platform Anda, misalnya

Windos atau Linux). Setelah diunduh, import ke dalam Eclipse seperti Anda

mengimport proyek-proyek pada website ini, yaitu dengan File -> Import ->

General -> Existing Projects Into Workspace -> Select Archieve File -> (file zip

SWT hasil download) -> Finish. Langkah ini hanya dilakukan satu kali saja.

Buat proyek baru dari Eclipse, File -> New -> Java Project, kemudian isi Project

Name dengan HelloSWT dan click Next

Page 368: Java With Eclipse

Kita harus menambahkan pustaka SWT ke dalam proyek kita.

1. Jika Anda menggunakan SWT bawaan Eclipse seperti di atas, pilih halaman

Libraries. Klik Add External JAR, kemudian navigasi ke direktori plugin di

dalam instalasi Eclipse Anda. Misalnya di komputer saya, direktori itu berada di

C:\eclipse\plugins. Pilih JAR untuk SWT pada direktori tersebut. Namanya

tergantung dari sistem operasi Anda, misalnya pada Windows file ini bernama

org.eclipse.swt.win32.win32.x86_3.4.1.v3449c. Klik OK setelah file ini

ditambahkan di dalam folder Libraries.

Page 369: Java With Eclipse

1. Jika Anda mengunduh proyek SWT, pilih halaman Projects. Klik Add, kemudian

tambahkan org.eclipse.swt. Kemudian klik OK setelah proyek ini ditambahkan.

Page 370: Java With Eclipse

Folder proyek baru akan dibuat, seperti pada gambar berikut.

Page 371: Java With Eclipse

Kemudian klik kanan pada folder src, dan pilih New -> Class. Isi nama kelas

dengan HelloSWT, nama package dengan helloswt, dan tik "public static void

main(String[] args)" untuk membuat metode main() secara otomatis.

Ketik kode berikut ini di dalam metode main()

Display display = new Display();

Shell shell = new Shell(display);

shell.setLayout( new RowLayout());

Label label = new Label(shell, SWT.NONE);

label.setText("Hello, World!");

shell.pack();

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

label.dispose();

Perhatikan bahwa Display, Shell, RowLayout dan Label diberi tanda merah

sebagai tanda bahwa kesalahan program terjadi.

Page 372: Java With Eclipse

Sekarang pindahkan mouse Anda pada kesalahan pada kelas Display. Eclipse akan

memberi tahu Anda kesalahan apa yang terjadi. Pada contoh ini, kelas 'Display'

belum diketahui oleh Eclipse. Klik pada pilihan Import 'Display'

(org.eclipse.swt.widgets). Lihat sekarang di awal program Anda Eclipse

menambahkan import org.eclipse.swt.widgets.Display; secara otomatis.

Lakukan hal yang sama pada semua kesalahan, yaitu Shell, Label, RowLayout dan

SWT.NONE, sehingga tidak ada lagi kesalahan yang dilaporkan oleh Eclipse.

Page 373: Java With Eclipse

Untuk menjalankan program Anda, klik tombol Run seperti pada gambar berikut.

Berikut ini adalah tampilan program SWT pertama Anda.

Mengkompilasi dan Membuat Program SWT Berjalan di Luar Eclipse

Untuk bisa membuat program kita berguna untuk orang lain, kita harus bisa

mengkompilasi dan membuat program tersebut sebagai satu "paket". Proses ini disebut

menyebarkan (deploy). Pada Eclipse, program yang telah kita buat bisa kita deploy dalam

format JAR (Java Archive), yang menggabungkan semua file kelas yang sudah dikompilasi

beserta pustaka yang dibutuhkan untuk bisa menjalankan program tersebut.

Mari kita lihat contoh program HelloSWT yang sudah dibahas sebelumnya. Untuk men-

deploy program ini sehingga bisa dijalankan di luar Eclipse, lakukan langkah-langkah

berikut :

Page 374: Java With Eclipse

Klik kanan pada nama proyek Anda, kemudian pilih Export. Setelah itu kotak

dialog akan terbuka. Kemudian pilih Runnable JAR File dari dalam folder Java.

Pilih HelloSWT - HelloSWT pada Launch configuration dan juga masukkan di

mana file JAR hasilnya akan ditempatkan. Jika nama direktori tidak diberikan,

Eclipse akan meletakkan file JAR tersebut pada direktori di mana workspace

berada, yaitu di mana data proyek ditempatkan. (Misalnya, di komputer saya

proyek HelloSWT berada pada direktori C:\belajarjava.lyracc.com\HelloSWT,

maka file JAR tersebut akan diletakkan pada direktori C:\belajarjava.lyracc.com\)

Klik Finish, kemudian OK untuk setiap pertanyaan yang mungkin muncul

berikutnya. Sekarang, gunakan Windows Explorer untuk mencari di mana file JAR

hasilnya berada. Double click pada file tersebut dan Anda akan bisa

menjalankannya langsung seperti aplikasi lainnya. Anda bisa juga mengirimkan file

Page 375: Java With Eclipse

ini melalui email atau mengkopinya melalui flash disk ke teman atau kerabat Anda

tanpa perlu menyertakan Eclipse.

CATATAN : Ingat bahwa SWT bergantung penuh pada sistem operasi yang Anda

gunakan, karena SWT menggunakan widget bawaan sistem operasi seperti

dijelaskan pada bagian sebelumnya. Ketika Anda membuat program ini pertama

kali, Anda menambahkan pustaka SWT yang spesifik terhadap sistem operasi di

mana Eclipse berjalan. Artinya, jika Anda menambahkan pustaka

org.eclipse.swt.win32.win32.x86_3.4.1.v3449c, maka program JAR Anda hanya

akan bisa dijalankan pada sistem operasi Windows. Untuk bisa menjalankannya

pada sistem operasi lain, misalnya Linux, maka Anda harus menambahkan pustaka

SWT khusus Linux yang bisa diunduh secara terpisah atau menggunakan Eclipse

pada Linux.

Berikut ini adalah screen shot hasil jalannya program

Page 376: Java With Eclipse

Melihat Lebih Dekat HelloSWT

Secara umum aplikasi SWT membutuhkan beberapa langkah sebagai berikut :

1. Buat Display baru

2. Buat satu atau lebih Shell

3. Buat manager layout untuk Shell baru

4. Buat widget di dalam shell

5. Buka jendela shell

6. Buat perulangan pengirim event

7. Buang (dispose) display dan widget-widget lainnya

8. Tentunya import berbagai paket yang diperlukan oleh program

package helloswt;

import org.eclipse.swt.SWT;

import org.eclipse.swt.layout.RowLayout;

import org.eclipse.swt.widgets.Display;

import org.eclipse.swt.widgets.Label;

import org.eclipse.swt.widgets.Shell;

public class HelloSWT {

/**

* @param args

*/

public static void main(String[] args) {

Display display = new Display();

Shell shell = new Shell(display);

shell.setLayout( new RowLayout());

Label label = new Label(shell, SWT.NONE);

label.setText("Hello, World!");

shell.pack();

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

label.dispose();

}

}

Mari kita bahas satu per satu

Page 377: Java With Eclipse

1. Membuat Display baru

Membuat Display dapat dilakukan dengan membuat instansi dari objek bertipe Display,

misalnya seperti perintah berikut

Display display = new Display();

Display (tampilan) adalah objek maya yang merupakan kontainer induk semua komponen

GUI. Suatu tampilan sebetulnya tidak terlihat, akan tetapi komponen yang ditambahkan ke

dalam tampilan dapat dilihat. Biasanya, suatu aplikasi hanya membutuhkan satu tampilan.

2. Membuat Shell baru

Membuat Shell dapat dilakukan dengan membuat instansi dari objek bertipe Shell,

misalnya seperti perintah berikut

Shell shell = new Shell(display);

Pada pernyataan di atas, konstruktor Shell mengambil parameter yang merupakan induk

di mana shell ini akan diletakkan. Pada pernyataan di atas, kita membuat objek shell

bertipe Shell yang memiliki induk objek display.

Shell adalah jendela suatu aplikasi. Shell tertinggi adalah shell yang berada langsung di

bawah objek bertipe Display, dan merupakan jendela utama suatu aplikasi. Shell

sebenarnya adalah widget komposit, yaitu suatu widget yang terdiri dari beberapa widget

lain. Oleh karenanya shell juga bisa berisi shell lain. Untuk aplikasi sederhana yang

menampilkan kata "Hello World!", kita hanya membutuhkan satu shell saja yang kita

letakkan pada objek display.

3. Membuat manager layout untuk Shell baru

Untuk membuat manager layout, kita menggunakan metode instansi dari objek shell, yaitu

setLayout() yang mengambil parameter jenis layout yang akan disusun pada shell.

Misalnya,

shell.setLayout( new RowLayout());

Pada contoh di atas, kita memerintahkan objek shell untuk mengatur widget-widget di

dalamnya dalam urutan seperti baris. RowLayout adalah kelas layout yang digunakan objek

shell untuk mengatur objek widget di dalamnya.

Manager layout digunakan untuk mengatur secara otomatis peletakkan setiap widget. Pada

SWT, objek komposit seperti Shell tidak mengetahui bagaimana widget harus ditampilkan.

Oleh karenanya jika kita tidak memberikan manager layout kepada objek bertipe Shell,

maka tidak ada widget yang akan ditampilkan.

Bahasan khusus tentang layout cukup kompleks, dan karenanya kita akan bahas tersendiri

pada bagian selanjutnya.

Page 378: Java With Eclipse

4. Membuat widget di dalam shell

Widget adalah komponen pembangun GUI. Menu, tombol, teks, kotak cek, kotak input

teks, hingga kanvas, adalah contoh-contoh widget. Setiap widget memiliki sifat dan cara

kerja yang berbeda-beda. Pada contoh ini, kita menggunakan widget yang sangat

sederhana yang dinamakan Label. Widget ini digunakan untuk menampilkan teks,

misalnya digunakan sebagai label atau pertanyaan pada suatu input teks.

Pada contoh HelloSWT, kita gunakan konstruktor yang mengambil 2 parameter. Yang

pertama, adalah kontainer induk di mana Label ini akan ditampilkan, dan parameter kedua

adalah bit gaya.

Label label = new Label(shell, SWT.NONE);

Label ini kemudian kita isi dengan kata "Hello Word!" untuk menampilkan kata ini pada

jendela utama.

label.setText("Hello, World!");

5. Membuka jendela shell

Setelah Display, Shell, dan widget-widgetnya disiapkan, kita perlu secara eksplisit

memerintahkan shell untuk membuka jendela dan menggambar seluruh widget yang sudah

disiapkan. Untuk ini kita gunakan perintah

shell.open();

6. Membuat perulangan pengirim event

SWT mengharuskan kita untuk membuat sendiri perulangan untuk mengirimkan event.

Artinya, blok perintah berikut ini "harus" selalu ada dalam semua aplikasi SWT yang akan

kita buat.

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

7. Membuang (dispose) display dan widget-widget lainnya

Karena SWT bekerja langsung dengan widget bawaan sistem operasi, maka tanggung

jawab untuk menghapus komponen GUI ada di tangan programmernya. Hal ini disebabkan

widget tersebut berada di luar jangkauan pemulung memori Java yang hanya bisa meraih

objek yang dibuat dan dijalankan dalam JVM (Java Virtual Machine).

Untuk menghapus widget dari memori, kita dapat menggunakan metode dispose() yang

dimiliki oleh hampir semua widget SWT. Pada contoh program ini, kita memiliki 2 widget,

yaitu shell dan label, sehingga keduanya harus dihapus dengan menggunakan perintah

display.dispose();

label.dispose();

Page 379: Java With Eclipse

8. Mengimport berbagai paket yang diperlukan oleh program

Kita bisa menggunakan Eclipse untuk membantu kita mengimpor setiap paket (seperti

yang diilustrasikan pada bagian sebelumnya), atau menggunakan wildcard seperti import

org.eclipse.swt.widgets.*

Beberapa paket penting yang tersedia pada SWT :

Nama Paket Kegunaan

org.eclipse.swtBerisi kelas SWT, kumpulan konstanta, dan kelas-kelas pengecualian

dan kesalahan

org.eclipse.swt.widgets

Widget dan kelas-kelas yang berhubungan dengannya, misalnya

menu, kotak dialog, dukungan event, kelas super abstrak untuk

layout, dan sebagainya

org.eclipse.swt.events Event tingkat tinggi, pendengar event, dan colokan (adapter)

org.eclipse.swt.layoutKelas layout standar seperti FillLayout, RowLayout, GridLayout,

FormLayout, dan kelas-kelas lainnya

org.eclipse.swt.graphicsHuruf, warna, gambar, dan operasi grafik dasar, seperti untuk

menggambar garis dan lingkaran

org.eclipse.swt.custom

Widget buatan sendiri dan kelas lain yang dibuat khusus untuk

Eclipse; tidak bergantung pada platform apapun (implementasinya

sama pada semua sistem operasi)

org.eclipse.swt.dndKelas bantuan untuk melakukan drag-and-drop pada berbagai

platform

org.eclipse.swt.accessibilityPerluasan untuk memberikan bantuan dan dukungan untuk orang

cacat

org.eclipse.swt.printing Dukugan printer dan kotak dialog untuk melakukan percetakan

org.eclipse.swt.program

Berisi kelas Program, untuk mempermudah pada saat program

dijalankan, pemetaan dokumen, dan icon; juga dibuat untuk Eclipse,

akan tetapi sangat bergantung pada platformnya (implementasinya

bisa berbeda untuk setiap sistem operasi)

org.eclipse.swt.browser

Menyediakan widget sebagai web broswer, dan kelas-kelas yang

mendukungnya. Menggunakan browser bawaan sistem operasi untuk

implementasinya

Page 380: Java With Eclipse

org.eclipse.swt.awtMenyediakan dukungan untuk membuat UI yang berisi campuran

widget dari SWT dan AWT

org.eclipse.swt.win32.oleHanya tersedia pada Windows; menyediakan fasilitas untuk

mengakses OLE (Object Linking and Embedding)

Pengenalan Konsep Widget pada SWT

Secara tradisional, widget bisa dibayangkan seperti perangkat abstrak yang berguna untuk

melakukan tugas tertentu. Istilah ini popules dalam bidan ekonomi. Para pembuat software

meminjam istilah ini untuk menyebut suatu paket program pembuat GUI (grahical user

interface). SWT adalah kepanjangan dari Standard Widget Toolkit karena widget

merupakan dasar pembangun suatu aplikasi yang dibangun dengan menggunakan SWT.

Apa itu widget?

Widget adalah elemen GUI yang dapat berinteraksi dengan user. Widget mengatur dan

menentukan kondisinya sendiri dengan memggunakan kombinasi beberapa operasi grafik.

Dengan menggunakan mouse atau keyboard, user bisa mengubah kondisi suatu widget.

Ketika kondisi suatu widget berubah, baik diubah oleh user ataupun diubah oleh suatu

kode program, maka widget akan menggambar dirinya kembali untuk merefleksikan hasil

perubahannya.

Siklus hidup widget

Widget memiliki siklus hidup sendiri. Widget dibuat oleh programmer dan dibuang ketika

tidak lagi dibutuhkan. Karena siklus hidup suatu widget sangat penting untuk memahami

SWT, maka kita akan bahas lebih jauh pada bagian ini.

1. Membuat Widget

Widget dibuat dengan menggunakan konstruktornya, atau dengan kata lain membuat

instansi suatu kelas. Ketika widget dibuat, widget akan mengambil sumber daya komputer

(memori, prosesor) dari sistem operasi. Artinya semua widget akan berada di level sistem

operasi, sehingga unjuk kerja dan pengaturan memori akan lebih optimal.

Konstruktor akan mengambil argumen yang biasanya tidak akan bisa diubah setelah

widget dibuat. Ada 4 kemungkinan konstruktor dari suatu jenis widget :

Widget()

Widget(Widget induk)

Widget(Widget induk, int bitGaya)

Widget(Widget induk, int bitGaya, int posisi)

Widget tidak bisa dibuat tanpa induk. Ini bisa dilihat dari pengenalan tentang widget di

bagian sebelumnya bagaimana widget tersusun secara hirarkis. Jenis induknya tergantung

Page 381: Java With Eclipse

dari jenis widgetnya. Misalnya, induk dari suatu menu harus berupa menu, dan tidak bisa

berupa tombol. Kompiler akan mengecek apakah induk suatu widget sesuai dengan

tipenya, jika tidak, kompiler akan menampilkan pesan kesalahan. (Pada beberapa platform,

kita dibolehkan untuk mengubah induk suatu widget. Akan tetapi SWT tidak memiliki

metode getParent() pada kelas-kelas Widgetnya. Ini disebabkan karena kompleksitasnya

yang mengharuskan kita untuk melakukan type cast kepada tipe induk yang benar.)

Bit gaya (style) adalah nilai bit integer yang digunakan untuk mengatur perilaku dan

tampilan suatu widget. Biasanya bit gaya hanya dibutuhkan pada saat widget dibuat,

misalnya memilih jenis editor multi baris atau baris tunggal. Karena atribut ini tidak bisa

diubah setelah widget dibuat, maka gaya suatu widget juga tidak bisa diubah setelah

widget diinstansikan.

Semua gaya widget dikumpulkan sebagai konstanta pada kelas org.eclipse.swt.SWT.

Kita bisa menggambungkan beberapa gaya dengan menggunakan operasi OR. Misalnya

kode berikut akan membuat widget teks multi baris yang memiliki tepi serta scroll bar

horizontal dan vertikal.

Text teks = new Text (induk, SWT.MULTI | SWT.V_SCROLL | SWT.H_SCROLL |

SWT.BORDER);

Gaya suatu widget bisa kita ambil setelah widget tersebut dibuat dengan menggunakan

metode instansi getStyle(). Untuk mengujinya, kita bisa menggunakan operasi AND

yang jika hasilnya tidak nol, maka widget tersebut memiliki gaya yang diuji. Misalnya,

kode berikut menguji apakah suatu widget teks memiliki tepi, kemudian mencetak hasilnya

pada konsol.

if ((teks.getStyle() & SWT.BORDER) != 0)

System.out.println("teks memiliki tepi");

else

System.out.println("teks tidak memiliki tepi");

Paremeter posisi memungkinkan kita membuat widget baru pada suatu induk di posisi

tertentu. Jika tidak diberikan, maka secara otomatis widget baru akan ditempatkan di akhir

posisi. Hal ini akan lebih memudahkan kita untuk membuat widget pada urutan tertentu,

misalnya membuat menu dengan urutan tertentu.

2. Membuang widget

Ketika widget tidak lagi dibutuhkan, termasuk ketika program selesai, maka widget harus

dihapus secara eksplisit dengan menggunakan metode instansi dispose(). Metode ini

akan menyembunyikan widget, menghapus widget yang ditampungnya, dan membuat

semua referensi di dalamnya menjadi null.

Page 382: Java With Eclipse

Jika metode dispose() tidak dipanggil setelah program selesai dijalankan, maka hal ini

akan menyebabkan kebocoran memori, di mana memori pada sistem operasi lambat laun

akan habis dipenuhi oleh widget yang tidak dihapus setelah program selesai.

Memanggil metode dispose() untuk widget yang sudah dibuang, akan melemparkan

pengecualian SWTException. Untuk mengetahui apakah suatu widget sudah dihapus, kita

bisa menggunakan metode instansi isDisposed().

Ada dua aturan yang penting untuk diingat :

Jika Anda membuat widget, maka pastikan Anda menghapusnya. Atau dengan kata

lain, semua widget yang Anda buat dengan konstruktor wajib dihapus. Akan tetapi

jika Anda mendapatkan widget dari metode instansi suatu widget, misalnya Font

huruf = kontrol.getFont(), maka Anda tidak perlu menghapusnya

Jika Anda menghapus suatu widget, maka semua widget di bawahnya akan dihapus

juga. Atau dengan kata lain, jika Anda menghapus suatu Shell, maka isi seluruh

Shell akan dihapus secara otomatis. Demikian juga halnya dengan menu, jika Anda

menghapus menu utama, maka semua sub-menu akan otomatis dihapus.

Event dan Listener

Event

Event adalah inti dari pemrograman GUI. GUI program tidak memiliki alur apa

yang akan terjadi ketika program dijalankan, dalam arti langkah per langkah dari

awal hingga akhir. Akan tetapi, program harus bisa bereaksi akan berbagai event

(kejadian) yang bisa berasal dari mouse atau keyboard. User bisa menekan tombol

keyboard apa saja, menggeser mouse, atau menekan tombol mouse. User bisa

melakukannya kapan saja, dan komputer harus bisa bereaksi dengan tepat.

Dalam Java, event dilambangkan oleh objek. Ketika suatu event terjadi, sistem

akan mengumpulkan informasi yang sesuai dengan even tersebut, kemudian

membuat objek yang berisi informasi ini. Jenis even berbeda dilambangkan oleh

objek dengan kelas yang berbeda pula. Setelah objek event dibuat, ia akan

diberikan sebagai parameter pada subrutin yang ditugaskan untuk menangani event

tersebut. Dengan menulis subrutin ini, programmer bisa memberi tahu apa yang

harus dilakukan jika mouse diklik atau keyboard ditekan, misalnya.

Sebagai programmer Java, kita hanya melihat even dari sisi yang lebih umum.

Banyak sekali hal yang terjadi antara tombol ditekan hingga subrutin yang kita buat

melakukan tugasnya. Secara garis besar, dalam metode main() kita harus

menuliskan perulangan dalam bentuk seperti :

Page 383: Java With Eclipse

ketika program masih berjalan:

Tunggu hingga even berikutnya terjadi

Panggil subrutin untuk menangai event tersebut

Perulangan ini disebut perulangan event. Pada SWT kita harus menulis sendiri

perulangan event ini pada metode main() kita dalam bentuk

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

SWT memiliki dua jenis event, yaitu event tanpa tipe dan event bertipe.

Event tanpa tipe diwakili oleh kelas Event. Kelas ini menyimpan informasi yang

berbeda-beda tergantung dari jenis. Akan tetapi secara umum semua event jenis

apapun memiliki informasi berikut :

Informasi Penjelasan

display Display di mana event ini terjadi

widget Widget di mana event ini terjadi

type Jenis event yang terjadi

Jenis event yang terjadi beserta penjelasannya dirangkum dalam tabel berikut ini :

Jenis Event Penjelasan

SWT.KeyDown Tombol ditekan

SWT.KeyUp Tombol dilepaskan

SWT.MouseDown Tombol mouse ditekan

SWT.MouseUp Tombol mouse dilepaskan

SWT.MouseMove Mouse berpindah posisi

SWT.MouseEnter Mouse masuk ke wilayah klien

SWT.MouseHover Mouse berada di sekitar klien

SWT.MouseExit Mouse keluar dari wilayah klien

SWT.MouseDoubleClick Mouse di-double click

SWT.Paint Suatu widget diberi perintah untuk menggambar dirinya

SWT.Move Posisi suatu widget berubah

SWT.Resize Ukuran widget berubah

Page 384: Java With Eclipse

SWT.Dispose Widget dihapus

SWT.SelectionSuatu aksi pemilihan dilakukan dalam widget (misalnya memilih

item pada drop down list

SWT.DefaultSelection Ada pilihan awal pada suatu widget

SWT.FocusIn Widget menerima fokus dari keyboard

SWT.FocusOut Widget kehilangan fokus dari keyboard

SWT.Expand Item pada pohon dikembangkan

SWT.Collapse Item pada pohon ditutup

SWT.Iconify Jendela Shell diminimasi

SWT.Deiconify Jendela Shell dibuka (restore)

SWT.Close Jendela Shell ditutup (dengan tombol X)

SWT.Show Widget bisa dilihat

SWT.Hide Widget disembunyikan

SWT.Modify Teks berubah pada suatu kontrol

SWT.Verify Input teks sedang diverifikasi

SWT.Activate Widget sedang diaktifkan

SWT.Deactivate Widget dimatikan

SWT.Help User meminta pertolongan tentang suatu widget

SWT.DragDetect Aksi drag-and-drop dideteksi

SWT.MenuDetectUser menekan tombol kik kanan untuk mengaktifkan menu

konteks

SWT.Arm Item pada menu digambarkan

SWT.Traverse Navigasi pada keyboard dideteksi

SWT.HardKeyDown Tombol pada hardware ditekan (untuk perangkat genggam)

SWT.HardKeyUp Tombol pada hardware dilepas (untuk perangkat genggam)

Suatu event juga bisa memiliki tipe. Artinya event yang dihasilkan merupakan

objek bertipe suatu kelas, misalnya MouseEvent, bukan hanya bertipe kelas Event

yang jenisnya disimpan dalam variabel tertentu. Event bertipe mengikuti pola

Page 385: Java With Eclipse

JavaBeans standar. Kelas-kelas ini terdapat dalam paket

org.eclipse.swt.events

Tabel berikut menggambarkan perbandingan antara event bertipe dan jenis event

dari event tanpa tipe.

Event Bertipe Jenis Event Tanpa Tipe

ArmEvent SWT.Arm

ControlEventSWT.Move

SWT.Resize

DisposeEvent SWT.Dispose

FocusEventSWT.FocusIn

SWT.FocusOut

HelpEvent SWT.Help

KeyEventSWT.KeyDown

SWT.KeyUp

MenuEventSWT.Hide

SWT.Show

ModifyEvent SWT.Modify

MouseEvent

SWT.MouseDoubleClick

SWT.MouseDown

SWT.MouseUp

MouseEvent SWT.MouseMove

MouseEvent

SWT.MouseEnter

SWT.MouseExit

SWT.MouseHover

PaintEvent SWT.Paint

SelectionEventSWT.DefaultSelection

SWT.Selection

ShellEvent

SWT.Activate

SWT.Close

SWT.Deactivate

Page 386: Java With Eclipse

SWT.Iconify

SWT.Deiconify

TraverseEvent SWT.Traverse

TreeEventSWT.Collapse

SWT.Expand

VerifyEvent SWT.Verify

Pertanyannya kenapa ada dua jenis event yang berbeda?

Pada versi awal SWT, hanya ada satu jenis yaitu jenis tanpa tipe. Setelah diskusi

yang cukup antar beberapa programmer Eclipse, komunitas user SWT dan

developernya, maka akhirnya diputuskan untuk menambahkan jenis bertipe seperti

pada JavaBeans. Alasannya adalah untuk memudahkan pemrograman SWT bagi

programmer yang sudah terlanjur terbiasa dengan AWT/Swing. Jenis tanpa tipe

masih tetap ada seperti biasa.

Listener

Supaya suatu event berarti, suatu program harus bisa mendeteksi event dan bereaksi

akan event tersebut. Untuk mendeteksi suatu event, suatu program harus

mendengarkannya. Mendengarkan event ini dilakukan oleh objek yang berna,a

pendengar event (event listener). Objek listener harus memiliki metode instansi

untuk menangani event yang didengarkannya. Bentuknya bervariasi tergantung dari

jenis event yang ditanganinya.

Ada beberapa hal detail yang harus diingat untuk bisa bekerja dengan event.

Beberapa langkah yang harus diingat :

1. Menambahkan import paket yang dibutuhkan, misalnya "org.eclipse.swt.events"

2. Mendeklarasikan kelas yang mengimplementasikan interface suatu listener

3. Menambahkan aksi yang dilakukan oleh kelas baru tersebut. Aksi ini adalah aksi

yang dilakukan untuk menangani suatu event

4. Mendaftarkan event tersebut ke komponen yang mungkin memberikan event.

Objek apapun bisa bertindah sebagai event listener asalkan ia

mengimplementasikan interface yang tepat. Suatu komponen dapat mendengarkan

event yang dihasilkannya sendiri. Suatu kelas dapat dibuat secara khusus hanya

untuk mendengarkan suatu event. Kebanyakan orang menganggap lebih mudah

untuk menggunakan kelas bertingkat anonim untuk mendengarkan suatu objek.

(Kelas bertingkat anonim telah dibahas sebelumnya di sini).

Page 387: Java With Eclipse

Seperti hanya Event, SWT memiliki dua jenis listener : tanpa tipe dan bertipe.

Masing-masing digunakan untuk menangani event tanpa tipe dan event bertipe.

Listener Tanpa Tipe (untuk menangani event tanpa tipe)

nterface generik yang digunakan untuk menangai event tanpa tipe dinamakan

Listener. Listener tanpa tipe dapat ditambahkan pada suatu widget dengan

menggunakan metode addListener().

Metode addListener() memiliki bentuk seperti

addListener(int jenisEvent, Listener listener)

Metode ini akan menambah listener ke dalam koleksi listener yang akan

dipanggil ketika event tipe tertentu terjadi. Ketika suatu event terjadi, maka listener

akan dipanggil dengan menggunakan metode handleEvent().

Contoh berikut mengilustrasikan bagaimana menambah suatu Listener ke dalam

suatu widget, yang akan dipanggil ketika event SWT.Dispose terjadi.

widget.addListener(SWT.Dispose, new Listener() {

public void handleEvent(Event event) {

// widget was disposed

}

});

Perhatikan bahwa bentuk di atas menggunakan kelas anonim yang langsung

diturunkan dari interface Listener. Di dalam kelas anonim tersebut, metode

handleEvent() harus diimplementasikan, di mana implementasinya adalah tugas

yang harus dilakukan ketika widget dihapus.

Jika beberapa listener ditambahkan, maka mereka akan dipanggil dengan urutan

ketika mereka ditambahkan (first in first called). Artinya listener pertama diberi

kesempatan untuk mengolah event (mungkin untuk memfilter sejumlah data)

sebelum listener lain melakukan tugasnya. Kita juga bisa menambah listener yang

sama beberapa kali, yang artinya listener tersebut akan dipanggil beberapa kali.

Kita bisa menghapus listener dari suatu widget dengan menggunakan metode

removeListener(). Bentuknya adalah sebagai berikut

removeListener(int jenisEvent, Listener listener)

Untuk menghapus suatu listener, kita harus memberikan instansi yang persisi sama

ketika listener tersebut ditambahkan. JIka beberapa instansi listener yang sama

sudah ditambahkan sebelumnya, maka kita harus menghapusnya berulang kali

sejumlah ia ditambahkan. Secara umum, menghapus suatu listener mungkin tidak

Page 388: Java With Eclipse

diperlukan. Listener akan diambil oleh pemulung memori ketika suatu widget

dihapus dan listener ini tidak lagi digunakan di manapun di dalam program.

Kita juga bisa memanggil suatu listener untuk melakukan tugas tertentu, yaitu

dengan menggunakan metode notifyListeners, yang memiliki bentuk seperti

notifyListeners(int jenisEvent, Event event)

jenisEvent adalah jenis event yang akan dilakukan, dan Event adalah objek yang

berisi event yang akan dilakukan. Hal penting yang harus dicatat adalah memanggil

metode notifyListeners() tidak menyebabkan event akan benar-benar terjadi.

Misalnya memanggil notifyListeners() dengan jenis SWT.MouseDown tidak

akan menyebabkan tombol terlihat seperti ditekan. Dan juga memanggil

notifyListeners() tidak menjamin bahwa semua data pada event terinisialisasi

seperti pada event sesungguhnya.

Listener Bertipe (untuk menangani Event bertipe)

Karena event bertipe dibuat mengikuti pola listener pada JavaBeans, penggunaan

listener bertipe sangat mirip dengan penggunaan listener pada AWT/Swing.

Misalnya untuk mendengarkan event ketika suatu widget dihapus, maka aplikasi

bisa menggunakan addDisposeListener(), yang memiliki bentuk seperti

addDisposeListener(DisposeListener listener)

Metode ini menambahkan listener ke dalam koleksi listener yang akan dipanggil

ketike suatu widget dihapus. Ketika suatu widget dihapus, maka listener akan

dipanggil dengan memanggil metode widgetDisposed().

Potongan kode berikut digunakan untuk menangani event ketika suatu widget

dihapus.

widget.addDisposeListener(new DisposeListener() {

public void widgetDisposed(DisposeEvent event) {

// widget was disposed

}

});

DisposeListener adalah suatu interface. Jika ada lebih dari satu metode yang

didefinisikan dalam listener, maka SWT akan secara otomatis menambahkan kelas

adapter yang berisi implementasi kosong dari metode-metode yang didefinisikan

pada interface tersebut. Misalnya interface SelectionListener memiliki dua

metode, yaitu widgetSelected() dan widgetDefaultSelected, yang keduanya

mengambil argumen bertipe SelectionEvents. Dalam hal ini kelas

Page 389: Java With Eclipse

SelectionAdapter tersedia untuk memberikan implementasi kosong untuk setiap

metode. Kelas adapter ini hanyalah sebagai kelas bantu yang sesuai dengan

konvensi pada listener JavaBeans.

Listener bertipe bisa dihapus dengan menggunakan metode penghapus untuk setiap

listner. Misalnya, listener untuk event ketika suatu widget dihapus bisa dibuang

dengan menggunakan removeDisposeListener(), yang memiliki bentuk

removeDisposeListener(DisposeListener listener)

listener adalah objek listener yang akan dihapus dari koleksi listener yang

dipanggil ketika widget dihapus.

Tabel berikut merangkum event bertipe, nama interface listener yang

menanganinya, serta metode pada interface tersebut, dibandingkan dengan listener

tanpa tipe.

Event

Bertipe

Interface

ListenerMetode

Jenis Event Tanpa

Tipe

ArmEvent ArmListener widgetArmed(ArmEvent) SWT.Arm

ControlEven

t

ControlListener

(dan

ControlAdapter)

controlMoved(ControlEvent)

controlResized(ControlEvent)

SWT.Move

SWT.Resize

DisposeEven

tDisposeListener widgetDisposed(DisposeEvent) SWT.Dispose

FocusEvent

FocusListener

(dan

FocusAdapter)

focusGained(FocusEvent)

focusLost(FocusEvent)

SWT.FocusIn

SWT.FocusOut

HelpEvent HelpListener helpRequested(HelpEvent) SWT.Help

KeyEventKeyListener (dan

KeyAdapter)

keyPressed(KeyEvent)

keyReleased(keyEvent)

SWT.KeyDown

SWT.KeyUp

MenuEvent

MenuListener

(dan

MenuAdapter)

menuHidden(MenuEvent)

menuShown(MenuEvent)

SWT.Hide

SWT.Show

ModifyEven

tModifyListener modifyText(ModifyEvent) SWT.Modify

Page 390: Java With Eclipse

MouseEvent

MouseListener

(dan

MouseAdapter)

mouseDoubleClick(MouseEvent)

mouseDown(MouseEvent)

mouseUp(MouseEvent)

SWT.MouseDoubleC

lick

SWT.MouseDown

SWT.MouseUp

MouseEventMouseMoveListe

nermouseMove(MouseEvent) SWT.MouseMove

MouseEvent

MouseTrackListe

ner (dan

MouseTrackAdapt

er)

mouseEnter(MouseEvent)

mouseExit(MouseEvent)

mouseHover(MouseEvent)

SWT.MouseEnter

SWT.MouseExit

SWT.MouseHover

PaintEvent PaintListener paintControl(PaintEvent) SWT.Paint

SelectionEve

nt

SelectionListener

(dan

SelectionAdapter)

widgetDefaultSelected(SelectionE

vent)

widgetSelected(SelectionEvent)

SWT.DefaultSelectio

n

SWT.Selection

ShellEventShallListener (dan

ShellAdapter)

shellActivated(ShellEvent)

shellClosed(ShellEvent)

shellDeactivated(ShellEvent)

shellIconified(ShellEvent)

shellDeiconified(ShellEvent)

SWT.Activate

SWT.Close

SWT.Deactivate

SWT.Iconify

SWT.Deiconify

TraverseEve

ntTraverseListener keyTraversed(TraverseEvent) SWT.Traverse

TreeEventTreeListener (dan

TreeAdapter)

treeCollapsed(TreeEvent)

treeExpanded(TreeEvent)

SWT.Collapse

SWT.Expand

VerifyEvent VerifyListener verifyText(VerifyEvent) SWT.Verify

Penanganan Mouse

Semua sistem operasi di mana SWT diimplementasikan mendukung perangkat tunjuk.

Biasanya berbentuk mouse, akan tetapi bisa jadi berupa trackball, trackpad, atau jenis

perangkat keras lainnya. Pada komputer genggam, perangkat tunjuk bisa jadi berupa stylus.

Untuk mempermudah pembahasan, kita akan gunakan mouse sebagai perangkat tunjuk,

tidak peduli bagaimana perangkat aslinya.

Posisi suatu mouse biasanya digambarkan dalam bentuk ikon kecil pada layar yang disebut

kursor. Hal ini berlaku untuk semua platform, kecuali pada Windows CE, karena perangkat

Page 391: Java With Eclipse

Windows CE biasanya berupa perangkat tunjuk "langsung", seperti stylys, yang tidak

membutuhkan kursor.

Mouse biasanya memiliki tiga tombol (kecuali pada Macintosh yang hanya memiliki satu

tombol, walaupun sebenarnya mouse lebih dari 1 tombol pun bisa digunakan). Mouse

digunakan untuk menunjuk, klik, geser (drag) dan memilih komponen kontrol GUI. Bisa

juga digunakan untuk menampilkan menu konteks yang biasanya ditampilkan dengan

mengklik kanan suatu mouse. Perilaku "drag-and-drop" mouse kurang lebih sangat

bergantung pada platformnya.

Ketika kita menggeser mouse, kursor akan berbah bentuk, tergantung dari kontrol apa di

bawahnya. Misalnya, widget teks akan mengubah tampilan kursor seperti huruf I untuk

menunjukkan bahwa user bisa mengetikkan sesuatu pada widget tersebut. Di dalam kursor,

ada titik pusat yang menunjukkan koordinat x dan y suatu mouse ketika event pada mouse

terjadi.

Event pada Mouse

Ketika tombol mouse ditekan atau mouse digerakkan, event mouse dibuat dan akan

diberikan kepada widget yang ada di bawahnya. Akan tetapi ketika tombol mouse ditekan

dan ditahan (terus ditekan), dan mouse berada di luar widget (mungkin ada di widget lain

atau pada aplikasi lain di desktop), eventnya akan diberikan kepada widget awal di mana

mouse tersebut ditekan. Pengalihan event sementara ini disebut pengambilan mouse.

Widget yang menerima event disebut widget pengambil. Pengambilan mouse terhadi

secara otomatis pada SWT. (Ini mungkin bukan sesuatu masalah, akan tetapi sebagai

informasi saja kepada Anda).

Tabel - Isi Event Mouse ketika suatu tombol mouse ditekan, dilepaskan atau mouse digeser

Nama

FieldPenjelasan

button Tombol yang ditekan atau dilepaskan

x Koordinat x ketika event terjadi

y Koordinat y ketika event terjadi

stateMaskBit mask yang menyatakan kondisi keyboard dan mouse sebelum event

terjadi

Ketika mouse ditekan atau dilepas, field bernama "button" akan diisi oleh tombol mana

yang ditekan. Tombol mouse diberi nomor dari kiri ke kanan yang dimulai dari 1. Untuk

Page 392: Java With Eclipse

user kidal (dan mengkonfigurasi sistem operasi untuk orang kidal), penomoran tombol

tetap sama, akan tetapi dimulai dari kanan ke kiri. Pemetaan tombol untuk orang kidal ini

tidak tampak oleh SWT dan aplikasi kita, karena dilakukan secara otomatis oleh sistem

operasi.

Ketika terjadi event pada mouse, koordinat x dan y-nya juga dilaporkan dalam event.

Koordinat yang dilaporkan adalah koordinat relatif widget ketika event tersebut dibuat

(bukan koordinat global layar atau aplikasi kita). Karena user mungkin telah memindahkan

mouse setelah menekan tombol, maka lokasi sebenarnya ketika event ini ditangani

mungkin berbeda dengan ketika event dibuat. Hal ini untuk menghindari program kita

untuk bertindak terlalu sensitif terhadap pergerakan mouse. (Jika kita membutuhkan lokasi

yang aktual pada saat-saat tertentu, kita bisa menggunakan metode getCursorLocation()

yang dimiliki oleh kelas Display.)

Event pada mouse juga menggunakan field lain yang dinamakan stateMask untuk

menunjukkan keadaan mouse. Seperti pada penanda tombol, stateMask berisi keadaan

mouse sebelum terjadinya suatu event. Misalnya, jika tidak ada tombol mouse yang

ditekan atau tombol keyboard lain yang ditekan ketika tombol kiri mouse ditekan, maka

event mouse akan diisi dengan button bernilai 1 dan stateMask bernilai 0. stateMask

tidak berisi "tombol 1". Akan tetapi ketika terjadi event lain ketika mouse kiri sedang

ditekan, maka stateMask akan berisi 1.

Keadaan suatu mouse dilambangkan oleh konstanta pada kelas SWT, seperti pada tabel

berikut :

stateMask Penjelasan

SWT.BUTTON1 Tombol 1 ditekan

SWT.BUTTON2 Tombol 2 ditekan

SWT.BUTTON3 Tombol 3 ditekan

SWT.BUTTON_MASK Bitwise-OR dari tombol-tombol yang ditekan

Berikut ini adalah event pada mouse yang disediakan oleh SWT. Seperti disebutkan pada

bagian sebelumnya, event dan listener SWT terdiri dari event/listener tanpa tipe dan

event/listener bertipe. Keduanya disarikan dalam tabel berikut :

Kelas

Event

(event

Interface/Kelas

Listener (listener

bertipe)

Metode (listener bertipe)Jenis event (event

tanpa tipe)

Penjelas

an

Page 393: Java With Eclipse

bertipe)

mouseDoubleClick(Mouse

Event)

SWT.MouseDouble

Click

Mouse

di-double

click

mouseDown(MouseEvent) SWT.MouseDown

Tombol

mouse

ditekan

MouseEv

ent

MouseListener

(dan

MouseAdapter)

mouseUp(MouseEvent) SWT.MouseUp

Tombol

mouse

dilepaska

n

MouseEv

ent

MouseMoveListe

nermouseMove(MouseEvent) SWT.MouseMove

Mouse

berpinda

h posisi

mouseEnter(MouseEvent) SWT.MouseEnter

Mouse

masuk ke

wilayah

klien

mouseExit(MouseEvent) SWT.MouseExit

Mouse

berada di

sekitar

klien

MouseEv

ent

MouseTrackListe

ner (dan

MouseTrackAda

pter)

mouseHover(MouseEvent) SWT.MouseHover

Mouse

keluar

dari

wilayah

klien

Mari kita lihat contoh penggunaan mouse event pada program berikut. Anda bisa

mengunduh program ini dan mengimportnya pada Eclipse di sini.

package net.lyracc.pelacakmouse;

import org.eclipse.swt.*;

Page 394: Java With Eclipse

import org.eclipse.swt.widgets.*;

public class PelacakMouse {

/**

* @param args

*/

public static void main(String[] args) {

// Membuat display dan shell baru

Display display = new Display();

Shell shell = new Shell(display);

// Kelas Listener baru, menggunakan listener tanpa tipe

Listener mouseListener = new Listener() {

// metode handleEvent pada interface Listener harus

diimplementasikan

public void handleEvent(Event e) {

String output = "UNKNOWN";

switch (e.type) {

case SWT.MouseDown: output = "DOWN"; break;

case SWT.MouseUp: output = "UP"; break;

case SWT.MouseMove: output = "MOVE"; break;

case SWT.MouseDoubleClick:

output = "DOUBLE";

break;

case SWT.MouseEnter: output="ENTER"; break;

case SWT.MouseExit: output = "EXIT"; break;

case SWT.MouseHover: output="HOVER"; break;

}

// Mengambil stateMask pada event, kemudian

menampilkannya

// dalam bentuk heksadesimal

output += ": stateMask=0x"

+ Integer.toHexString(e.stateMask);

// Jika tombol Ctrl ditekan, tambahkan CTRL pada

keluarannya

if ((e.stateMask & SWT.CTRL) != 0)

output += " CTRL";

Page 395: Java With Eclipse

// Jika tombol Alt ditekan, tambahkan ALT pada

keluarannya

if ((e.stateMask & SWT.ALT) != 0)

output += " ALT";

// Jika tombol Shift ditekan, tambahkan SHIFT pada

keluarannya

if ((e.stateMask & SWT.SHIFT) != 0)

output += " SHIFT";

// Jika tombol Command ditekan, tambahkan COMMAND pada

keluarannya

if ((e.stateMask & SWT.COMMAND) != 0)

output += " COMMAND";

// Jika tombol kiri mouse ditekan, tambahkan BUTTON1 pada

keluarannya

if ((e.stateMask & SWT.BUTTON1) != 0)

output += " BUTTON1";

// Jika tombol tengah mouse ditekan, tambahkan BUTTON2

pada keluarannya

if ((e.stateMask & SWT.BUTTON2) != 0)

output += " BUTTON2";

// Jika tombol kanan mouse ditekan, tambahkan BUTTON3

pada keluarannya

if ((e.stateMask & SWT.BUTTON3) != 0)

output += " BUTTON3";

// Mengambil field button pada event, kemudian

menampilkannya

// dalam bentuk heksadesimal

output += ", button=0x"

+ Integer.toHexString(e.button);

// Mengambil koordinat x dan y

output += ", x=" + e.x + ", y=" + e.y;

// Menampilkan pesan keluaran pada konsol

Page 396: Java With Eclipse

System.out.println(output);

}

};

// Tambahkan listener pada setiap event yang ingin kita pantau

shell.addListener(SWT.MouseDown, mouseListener);

shell.addListener(SWT.MouseUp, mouseListener);

shell.addListener(SWT.MouseMove, mouseListener);

shell.addListener(SWT.MouseDoubleClick, mouseListener);

shell.addListener(SWT.MouseEnter, mouseListener);

shell.addListener(SWT.MouseExit, mouseListener);

shell.addListener(SWT.MouseHover, mouseListener);

// Ubah ukuran jendela menjadi 200 x 200 piksel

shell.setSize(200, 200);

// Perintah "standar" SWT, harus ada pada setiap aplikasi SWT

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

}

Program di atas akan melacak aktivitas mouse kemudian menampilkannya pada konsol.

Jangan lupa untuk menambahkan pustaka SWT sebelum menjalankan program ini seperti

dibahas pada bagian ini.

Klik Run pada Eclipse dan jalankan sebagai Java Application. Setelah jendela aplikasi

yang kita buat keluar, coba jalankan mouse dan perhatikan "Console" pada Eclipse seperti

gambar berikut.

Page 397: Java With Eclipse

Penanganan Keyboard

Membuat API yang bisa dijalankan di berbagai platform untuk mengakses keyboard bukan

sesuatu yang mudah. Bahkan untuk platform yang sama tetapi dengan versi sistem operasi

atau kedaerahan (locale) yang berbeda, karakter bisa dimasukkan dengan cara yang

berbeda.

Metode input, atau sering juga disebut IM (input method) atau IME (input method engine),

adalah mesin pengolah karakter yang disediakan oleh setiap sistem operasi. Tugasnya

untuk mengubah urutan tombol keyboard menjadi karakter yang bisa dibaca sesuai dengan

konfigurasi locale usernya. Misalnya, pada locale Jepang, tombol yang dimasukkan

diproses oleh IME sehingga user bisa memasukkan karakter Kanji.

Untungnya, kebanyakan kontrol (widget yang bisa berinteraksi dengan user) bisa

menangani keyboard tanpa perlu campur tangan aplikasi. Misalnya, ketika user

memasukkan karakter Kanji pada kontrol teks, kontrol tersebut akan mengolah karakter

dan menampilkannya. Jika ada menu konteks, maka menu ini akan secara otomatis

disediakan.

Ketika user menekan tombol pada mouse, maka event yang dihasilkan akan diberikan

kepada widget yang ada dibawahnya. Apa yang terjadi ketika tombol keyboard di tekan?

Komponen mana yang harus menangani event yang dihasilkan?

Ketika suatu tombol pada keyboard ditekan, tombol tersebut akan diberikan kepada

subsistem berikut dalam urutan :

1. Sistem jendela

Page 398: Java With Eclipse

2. Akselerator untuk menu

3. Traversal

4. Kontrol fokus

Artinya setiap subsistem diberi kesempatan untuk bereaksi terhadap tombol keyboard,

dalam urutan seperti di atas. Jika salah satu subsistem mampu menangani tombol tersebut,

maka tombol ini tidak akan diteruskan ke subsistem berikutnya. Dalam hal ini kita sebut

tombol tersebut telah digunakan.

Kita akan diskusikan setiap subsistem dari bawah ke atas, mulai dari kontrol fokus, karena

konsep ini adalah konsep dasar yang diperlukan dalam pemrograman GUI.

Kontrol Fokus

Program GUI menggunakan fokus input untuk menentukan komponen mana yang yang

harus menangani event dari keyboard. Pada suatu saat, hanya ada elemen pada layar yang

bisa menerima input, yaitu di mana semua event dari keyboard akan diarahkan. Mungkin

merupakan sesuatu yang baik untuk memberi user sedikit bantuan untuk mengetahui

apakah suatu komponen memiliki fokus input. Misalnya, jika komponen tersebut adalah

tempat mengetik pada program pengolah kata, maka biasanya kursornya akan berubah

menjadi bentuk I dan berkedip-kedip. Contoh lainnya adalah misalnya dalam suatu

formulir, input teks yang sedang dalam fokus memiliki warna latar belakang yang berbeda

dengan input teks yang tidak memiliki fokus.

Untuk memberikan fokus kepada suatu komponen, kita bisa menggunakan metode

setFocus() yang terdapat pada hampir semua widget. Jika kontrol adalah objek bertipe

suatu widget yang bisa menerima fokus dan sukses adalah variabel bertipe boolean, maka

sukses = kontrol.setFocus();

memberi perintah kepada kontrol untuk mencoba mengambil fokus input. Jika kontrol

atau salah satu komponen yang ditampungnya berhasil mengambil fokus, fungsi ini akan

mengembalikan true. Jika gagal, maka false akan dikembalikan.

Widget komposit (yaitu widget yang bisa berisi widget-widget lain) akan berusaha untuk

memberi fokus kepada widget yang ditampungnya sebelum mengambil fokus untuk

dirinya sendiri. Beberapa widget lain, seperti label, biasanya tidak mengambil fokus. Suatu

kontrol tidak bisa mengambil fokus jika ia tidak aktif atau disembunyikan, atau jika input

diblokade karena modalitas (atau karena kontrol lain tidak mengijinkan fokus berpindah

tempat).

Fungsi lain kontrol.isFocusControl() mengembalikan true jika kontrol tersebut sedang

memegang fokus atau false jika tidak. Kita juga bisa mencari tahu kontrol mana yang

Page 399: Java With Eclipse

sedang memiliki fokus dengan menggunakan metode pada kelas Display yaitu

Display.getFocusControl().

Berikut ini adalah daftar event dan listener yang berkaitan dengan fokus.

Kelas Event

(event

bertipe)

Interface/Kelas

Listener (listener

bertipe)

Metode (listener

bertipe)

Jenis event

(event tanpa

tipe)

Penjelasan

focusGained(FocusEvent) SWT.FocusIn

Widget

menerima

fokus dari

keyboardFocusEvent

FocusListener

(dan

FocusAdapter)

focusLost(FocusEvent) SWT.FocusOut

Widget

kehilangan

fokus dari

keyboard

Mari kita lihat contoh berikut ini untuk memahami lebih lanjut tentang fokus input. Kita

akan buat 2 widget yang berupa input teks. Ketika input teks atas menerima fokus, maka

judul aplikasi kita ganti dengan "Fokus pada input teks atas". Ketika input teks bawah

menerima fokus, maka judul aplikasi kita ganti dengan "Fokus pada input teks bawah".

Jika input teks atas kehilangan fokus, kita isi input teks yang kehilangan fokus dengan

"Saya kehilangan fokus", begitu input teks atas menerima fokus kembali, kita akan hapus

kalimat tersebut.

Contoh program ini dapat Anda unduh di sini, untuk diimport pada Eclipse. Pembahasan

detail tentang contoh program ini akan dibahas kemudian.

Event pada Tombol

Ketika suatu tombol pada keyboard ditekan, event tombol akan dibuat dan diberikan

kepada aplikasi kita. Akan tetapi, tergantung pada platformnya, kedaerahan (locale), dan

kombinasi tombol, ada kalanya event tidak terjadi. Misalnya, pada karakter Eropa yang

memiliki aksen, mesin pengolah karakter dari sistem operasi akan mengambil tombol

Page 400: Java With Eclipse

tersebut untuk diolah. Misalnya pada kedaerahan Jerman jika karakter ^ ditekan kemudian

diikuti dengan tombol e, maka karakter ê akan ditampilkan. Demikian juga jika tombol

bantu ditekan untuk mengolah bahasa Jepang, IME akan mengolah urutan karakter menjadi

karakter Kanji.

Dengan kata lain, event tingkat rendah seperti ini sangat bergantung pada platform dan

sistem operasi, sehingga tidak terlalu berguna untuk kebanyakan program. SWT

menyembunyikan event sistem operasi dan hanya menampilkan satu event tombol saja

setelah sistem operasi selesai mengolah tombol tersebut.

Berikut ini adalah daftar event dan listener yang berkaitan dengan tombol.

Kelas

Event

(event

bertipe)

Interface/Kelas

Listener (listener

bertipe)

Metode (listener

bertipe)

Jenis event

(event tanpa

tipe)

Penjelasan

keyPressed(KeyEvent) SWT.KeyDownTombol

ditekanKeyEvent

KeyListener (dan

KeyAdapter)keyReleased(keyEvent) SWT.KeyUp

Tombol

dilepaskan

Event SWT.KeyDown dan SWT.KeyUp merupakan representasi tingkat tinggi dari tombol

yang ditekan dan dilepaskan. Event-event ini berguna jika kita ingin mencegat suatu

tombol tertentu dan melakukan aksi khusus ketika tombol itu ditekan.

Berikut ini adalah isi event keyboard ketika ditekan/dilepaskan.

Nama

FieldPenjelasan

character Nilai Unicode dari karakter yang ditekan

keyCodeKonstanta yang menunjukkan tombol mana yang ditekan, misalnya

SWT.PAGE_UP

stateMask Melambangkan tombol tambahan, misalnya SWT.SHIFT

doitSuatu boolean yang bisa digunakan untuk membatalkan aksi penekanan

tombol

character berisi karakter yang kita masukkan lewat keyboard setelah diolah oleh sistem

operasi. Misalnya jika kita menekan tombol <a> maka character berisi 'a'. Jika tombol

<Shift> dan <a> ditekan, maka character berisi 'A'. Jika tombol <Ctrl> dan <a> ditekan,

Page 401: Java With Eclipse

maka character akan diisi karakter yang bersesuaian dengan Ctrl+a, yaitu karakter

dengan kode Unicode '\u0001' (atau SOH). Beberapa tombol seperti Enter, Backspace,

Tab, memiliki kode karakter Unicode tersendiri. SWT juga memiliki konstanta untuk

mewakili tombol-tombol ini, yaitu

Konstanta character Penjelasan

SWT.BS Tombol backspace

SWT.CR Tombol Enter

SWT.DEL Tombol Del

SWT.ESC Tombol Esc

SWT.LF Tombol LF

SWT.TAB Tombol Tab

keyCode berisi karakter yang tidak bisa diwakilkan dengan karakter Unicode, misalnya

tombol <F1>, tombol <PgUp>, tombol <Panah Atas>, dan lain-lain termasuk tombol

angka pada keypad dan tombol <+> <-> <*> pada keypad. Beberapa tombol tersebut

dilambangkan dalam konstanta sebagai berikut. Khusus untuk keypad, apabila tombol <+>

ditekan, maka selain keyCode berisi SWT.KEYPADD_ADD, character juga berisi '+'.

SWT.F

1SWT.F11 SWT.PAGE_DOWN

SWT.KEYPA

D_0

SWT.KEYPAD_EQ

UAL

SWT.F

2SWT.F12 SWT.HOME

SWT.KEYPA

D_1SWT.KEYPAD_CR

SWT.F

3SWT.F13 SWT.END

SWT.KEYPA

D_2SWT.HELP

SWT.F

4SWT.F14 SWT.INSERT

SWT.KEYPA

D_3SWT.CAPS_LOCK

SWT.F

5SWT.F15

SWT.KEYPAD_MULT

IPLY

SWT.KEYPA

D_4SWT.NUM_LOCK

SWT.F

6

SWT.ARROW_U

PSWT.KEYPAD_ADD

SWT.KEYPA

D_5

SWT.SCROLL_LO

CK

SWT.F

7

SWT.ARROW_D

OWN

SWT.KEYPAD_SUBT

RACT

SWT.KEYPA

D_6SWT.PAUSE

Page 402: Java With Eclipse

SWT.F

8

SWT.ARROW_LE

FT

SWT.KEYPAD_DECI

MAL

SWT.KEYPA

D_7SWT.BREAK

SWT.F

9

SWT.ARROW_RI

GHT

SWT.KEYPAD_DIVID

E

SWT.KEYPA

D_8

SWT.PRINT_SCRE

EN

SWT.F

10SWT.PAGE_UP SWT.KEYPAD_0

SWT.KEYPA

D_9

stateMask berisi tombol sebelum tombol ditekan, yang biasanya <Ctrl>, <Shift>, <Alt>,

<Command>. Pada kebanyakan keyboard hanya ada 3 tombol pertama, akan tetapi ada

juga yang memiliki lebih dari 3 tombol. Tombol-tombol ini disebut tombol pengubah.

SWT membuat tombol-tombol ini menjadi kode seperti

stateMask Penjelasan

SWT.MOD1Tombol pengubah pertama ditekan (biasanya SWT.CONTROL

pada Windows atau SWT.COMMAND pada Macintosh)

SWT.MOD2 Tombol pengubah kedua ditekan (biasanya SWT.SHIFT)

SWT.MOD3 Tombol pengubah ketiga ditekan (biasanya SWT.ALT)

SWT.MOD4 Tombol pengubah keempat ditekan (biasanya 0)

SWT.MODIFIER_MASK Gabungan dari keempatnya (menggunakan bitwise OR)

Dengan representasi seperti ini, maka SWT bisa dijalankan pada beberapa platform, dan

tidak bergantung dengan tombol apa yang ada pada suatu sistem operasi. Bayangkan jika

Anda ingin menggunakan <Control> + <b> untuk membuat karakter menjadi tebal, akan

tetapi tombol <Control> tidak tersedia pada Macintosh.

Untuk menguji tombol pengubah mana yang ditekan, kita bisa menggunakan bitwise AND,

misalnya (e.stateMask & SWT.SHIFT).

Berikut ini adalah contoh program pelacak keyboard yang akan melaporkan tombol apa

yang Anda tekan dan lepaskan. Contoh program ini dapat Anda unduh di sini untuk

mengimportnya ke dalam Eclipse. Jalankan program ini pada Eclipse, kemudian ketik apa

saja di program Anda, perhatikan "Console" di Eclipse akan penuh dengan berbagai

laporan tentang tombol yang ditekan dan dilepaskan.

Page 403: Java With Eclipse

Kadang kala dalam kondisi yang sangat langka, kita harus mengolah sendiri tombol kita

sebelum diolah oleh suatu wudget. Karena SWT menggunakan widget bawaan sistem

operasi, pengolahan tombol terjadi di level sistem operasi. Misalnya, ketika user mengetik

pada widget teks, listener SWT.KeyDown akan dijalankan, kemudian sistem operasi akan

memasukkan karakter dan menggambarnya kembali dijalankan oleh sistem operasi.

Dengan menggunakan doit, kita bisa membuang karakter tersebut untuk tidak

meneruskannya ke sistem operasi.

Contoh berikut akan menghalangi user untuk memasukkan karakter pada widget teks

dengan mengeset doit menjadi false setiap kali event SWT.KeyDown terjadi.

package com.lyracc.penghalangtombol;

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

public class PenghalangTombol {

/**

* @param args

*/

public static void main(String[] args) {

Display display = new Display();

Shell shell = new Shell(display);

Page 404: Java With Eclipse

Text text = new Text(shell, SWT.SINGLE | SWT.BORDER);

text.addListener(SWT.KeyDown, new Listener() {

public void handleEvent(Event event) {

event.doit = false;

}

});

text.pack();

shell.pack();

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

}

Traversal

Traversal atau penelusuran berarti memindahkan fokus dari satu kontrol ke kontrol lain.

Tombol traversal berbeda dengan tombol akselerasi dan tombol sistem jendela (yang akan

dibahas nanti), yaitu : Suatu kontrol bebas memilih apakah akan menjalankan operasi

traversal atau mengolah tombol yang ditekan. Jika kontrol memilih untuk menjalankan

operasi traversal maka tombol tersebut tidak akan diproses lebih lanjut.

Ada dua jenis traversal, yaitu mnemonik dan tab.

Traversal Mnemonik

Mnemonik biasanya tertulis sebagai karakter yang diberi garis bawah pada label suatu

widget. Suatu aksi akan dilakukan jika user menekan kombinasi tombol yang cocok

dengan mnemonik tersebut, biasanya dengan menekan tombol <Alt> bersamaan dengan

karakter yang digaris bawah tersebut.

Gambar berikut adalah contoh mnemonik pada program OpenOffice ketika kita membuka

kotak dialog Format Font

Page 405: Java With Eclipse

Jika kita menekan <Alt> + H maka tampilan Help akan ditampilkan.

Menekan tombol kombinasi untuk memanggil mnemonik sama dengan mengaktifkan

widget tersebut. Pada contoh di atas, mnemonik <Alt> + H diterapkan pada tombol, yang

artinya ketika kita menekan <Alt> + H, sama dengan kita menekan tombol Help. Beberapa

widget yang tidak bisa menerima pilihan, seperti label dan kotak grup masih bisa

menerima mnemonik, akan tetapi fungsinya hanya untuk memindahkan fokus ke widget

tersebut.

Bagaimana caranya menambahkan mnemonik? Mudah saja. Cukup tambahkan '&' di

depan karakter yang akan kita tandai sebagai mnemonik, kemudian masukkan string ini

sebagai argumen pada metode setText() suatu widget. Misalnya pada perintah berikut :

Button tombol1 = new Button(shell, SWT.PUSH);

tombol1.setText("&Klik saya");

akan membuat tombol dengan mnemonik <Alt> + K, seperti pada gambar berikut ini :

Untuk membuat mnemonik pada karakter '&' sendiri, gunakan '&&', misalnya "Ini && Itu"

akan menghasilkan "Ini & Itu"

Page 406: Java With Eclipse

Traversal Tab

Traversal tab didukung pada semua platform. Tidak seperti pada mnemonik, pada traversal

tab, kita tidak perlu mendefinisikan apa-apa, karena setiap platform memiliki cara sendiri

bagaimana memindahkan fokus dari satu widget ke widget lain. Misalnya, ketika kita

menekan tombol <Tab>, maka fokus akan otomatis pindah ke widget berikutnya. Ketika

sampai pada widget terakhir, maka fokus akan diulang dari widget pertama.

Nama traversal tab mungkin agak sedikit salah sasaran, karena sepertinya hanya tombol

<Tab> saja yang bisa digunakan untuk memindahkan fokus dari satu widget ke widget

lain. Pada beberapa platform, menekan tombol panah juga memindahkan fokus.

Beberapa tombol lain seperti <Esc> digunakan untuk menutup kotak dialog, dan

sebenarnya termasuk dalam tombol traversal tab juga.

Berikut ini adalah daftar event dan listener yang berkaitan dengan traversal.

Kelas Event

(event

bertipe)

Interface/Kelas

Listener

(listener

bertipe)

Metode (listener bertipe)

Jenis event

(event tanpa

tipe)

Penjelasan

TraverseEvent TraverseListener keyTraversed(TraverseEvent) SWT.Traverse

Navigasi

pada

keyboard

dideteksi

Berikut ini adalah isi event ketika event traversal terjadi.

Nama Field Penjelasan

detail Detail traversal yang terjadi

doit Suatu boolean yang bisa digunakan untuk membatalkan aksi traversal

detail berisi salah satu dari nilai-nilai berikut.

isi detail Penjelasan

SWT.TRAVERSE_ESCAPE

Traversal yang terjadi ditutupnya suatu kotak

dialog, misalnya dengan menekan tombol

Cancel atau tombol <Esc>

SWT.TRAVERSE_RETURNTraversal yang terjadi ketika kotak dialog

selesai diisi misalnya setelah menekan tombol

Page 407: Java With Eclipse

OK atau tombol <Enter>

SWT.TRAVERSE_TAB_PREVIOUSTraversal yang terjadi ketika fokus pindah ke

group tab sebelumnya

SWT.TRAVERSE_TAB_NEXTTraversal yang terjadi ketika fokus pindah ke

group tab sesudahnya

SWT.TRAVERSE_ARROW_PREVIOUSTraversal yang terjadi ketika fokus pindah ke

item sebelumnya

SWT.TRAVERSE_ARROW_NEXTTraversal yang terjadi ketika fokus pindah ke

item sesudahnya

SWT.TRAVERSE_MNEMONIC Traversal mnemonik terjadi

SWT.TRAVERSE_PAGE_PREVIOUSTraversal yang terjadi ketika untuk pindah ke

halaman sebelumnya pada kotak dialog

SWT.TRAVERSE_PAGE_NEXTTraversal yang terjadi ketika untuk pindah ke

halaman berikutnya pada kotak dialog

SWT.TRAVERSE_NONE Traversal tidak terjadi

Variabel detail bukan hanya untuk dibaca akan tetapi kita juga bisa mengisinya apabila

kita ingin mengubah jenis traversal. Misalnya kita ingin mengubah tombol <Enter> bukan

untuk menutup dialog akan tetapi untuk memindahkan fokus ke widget lain, kita bisa

mengisi variabel detail dengan SWT.TRAVERSE_ARROW_PREVIOUS.

doit digunakan untuk membatalkan traversal jika variabel ini diisi false. Akan tetapi lihat

bahwa pada variabel detail juga bisa SWT.TRAVERSE_NONE. Apa perbedaannya? Ingat

bahwa tombol yang tidak digunakan untuk traversal akan diberikan kepada widget yang

menerima traversal untuk diolah lebih lanjut.

Artinya jika event doit kita isi dengan true, traversal akan dilakukan dan tombol akan

"dikonsumsi" (tidak diberikan kepada widget untuk diproses kembali). Jika doit kita isi

denga false, traversal tidak dilakukan dan tombol akan diberikan kepada widget untuk

diproses.

Apa yang terjadi jika detail juga diisi dengan SWT.TRAVERSE_NONE? Jika detail diisi

dengan SWT.TRAVERSE_NONE maka widget tidak akan melakukan traversal, tidak peduli

apakah isi doit berisi true atau false. Akan tetapi, variabel doit menentukan apakah

tombol akan diberikan kepada widget untuk diproses.

Page 408: Java With Eclipse

Jadi jika doit berisi false dan detail berisi SWT.TRAVERSE_NONE, maka tombol akan

diberikan kepada widget, dan traversal tidak dilakukan. Akan tetapi jika doit berisi true

dan detail berisi SWT.TRAVERSE_NONE, maka traversal tidak dilakukan, dan tombol akan

dikonsumsi dan tidak akan diberikan kepada widget.

Berikut ini adalah contoh penggunaan traversal.

Program ini akan membuat 6 tombol. Coba tekan tombol <Tab>. Fokus akan pindah ke

tombol berikutnya setiap kali Anda menekan tombol <Tab>. Ketika Anda melewati tombol

4 atau tombol 6, akan tercetak "Button {4} ditelusuri". Jika Anda menekan tombol <Shift>

+ <Tab> fokus akan berpindah ke tombol sebelumnya. Akan tetapi jika Anda sampai pada

tombol 4 atau tombol 6, event traversal yang terjadi akan ditangkap dan traversal akan

diabaikan. Akibatnya Anda tidak akan bisa pindah dari tombol 4 ke tombol 3 atau tombol 6

ke tombol 5. Pada saat yang sama di konsol akan tercetak "Anda tidak bisa kembali!".

Program lengkapnya adalah sebagai berikut, yang bisa diunduh di sini.

package com.lyracc.traversalkustom;

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

import org.eclipse.swt.events.*;

Page 409: Java With Eclipse

public class TraversalKustom {

/**

* @param args

*/

public static void main(String[] args) {

Display display = new Display();

Shell shell = new Shell(display);

shell.setSize(300, 200);

Button b1 = new Button(shell, SWT.PUSH);

Button b2 = new Button(shell, SWT.PUSH);

Button b3 = new Button(shell, SWT.PUSH);

Button b4 = new Button(shell, SWT.PUSH);

Button b5 = new Button(shell, SWT.PUSH);

Button b6 = new Button(shell, SWT.PUSH);

b1.setBounds(10,10,50,50);

b2.setBounds(100,10,50,50);

b3.setBounds(200,10,50,50);

b4.setBounds(10,100,50,50);

b5.setBounds(100,100,50,50);

b6.setBounds(200,100,50,50);

b1.setText("1");

b2.setText("2");

b3.setText("3");

b4.setText("4");

b5.setText("5");

b6.setText("6");

TraverseListener traverseListener = new TraverseListener() {

public void keyTraversed(TraverseEvent e) {

if (e.detail == SWT.TRAVERSE_TAB_PREVIOUS) {

System.out.println("Anda tidak bisa kembali!");

e.doit = false;

}

System.out.println(e.widget + " ditelusuri");

}

};

b4.addTraverseListener(traverseListener);

b6.addTraverseListener(traverseListener);

Page 410: Java With Eclipse

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

}

Tombol Akselerator dan Tombol Sistem Jendela

Tombol Akselerator

Akselerator adalah tombol shortcut ke salah satu menu, misalnya pada kebanyakan

program pengolah kata menekan tombol <Ctrl> dan S artinya menyimpan teks yang

sedang diedit. Akselerator biasanya berupa rangkaian tombol yang ditekan bersamaan, bisa

2 tombol seperti contoh <Ctrl> di atas atau mungkin lebih, misalnya pada Firefox, tombol

<Ctrl> + <Shift> + J membuka konsol kesalahan (error console).

Akselerator selalu berhubungan dengan menu, sehingga akselerator bersifat global, yang

berarti kita bisa menekan tombol akselerator dari dalam widget manapun yang sedang aktif

pada saat itu. Ketika akselerator dipanggil, maka tombol-tombol yang ditekan akan

dikonsumsi langsung oleh aplikasi dan tidak diberikan kepada widget apa-apa.

Akselerator direpresentasikan pada SWT dalam bentuk kode integer tertentu. Kode integer

tersebut berisi gabungan beberapa tombol, yaitu tombol pengubah (seperti <Ctrl>, <Shift>,

<Alt>) dan satu tombol lain yang berupa karakter atau keyCode (seperti tanda panah, F1

hingga F15, <Esc>, dll). Lihat bahasan tentang fokus kontrol untuk mengerti lebih jauh

tentang bagaimana penanganan tombol pada SWT. Tombol akselerator tidak bisa hanya

terdiri dari tombol pengubah saja.

Berikut ini adalah beberapa contoh akselerator.

Akselerator Rangkaian Tombol

SWT.CONTROL + 'A' <Ctrl> + <A>

SWT.SHIFT +

SWT.ARROW_UP<Shift> + tombol panah ke atas

SWT.MOD1 + 'S'Tombol pengubah pertama (biasanya <Ctrl> atau

<Command>) + <S>

SWT.MOD1 + SWT.MOD2 +

'B'

Tombol pengubah pertama + Tombol pengubah kedua +

<B>

Page 411: Java With Eclipse

Akselerator diberikan kepada menu atau toolbar dengan menggunakan metode

setAccelerator(int kodeAkselerator) di mana kodeAkselerator adalah kode

akselerator seperti dicontohkan pada tabel di atas. Untuk mereset akselerator, isi

kodeAkselerator dengan 0.

Untuk mengambil tombol akselerator suatu menu atau toolbar bisa digunakan dengan

menggunakan metode getAccelerator() yang mengembalikan nilai integer.

Misalnya jika item adalah suatu item pada menu, maka potongan kode berikut akan

menambahkan akselerator pada item tersebut :

item.setText("Pilih &Semua\tCtrl+S");

item.setAccelerator(SWT.MOD1 + 'S');

item.addListener(SWT.Selection, new Listener() {

public void handleEvent(Event e) {

System.out.println("Item dipilih.");

}

});

Karakter '\t' di dalam setText memberi tahu SWT bahwa karakter setelah itu adalah

tombol akseleratornya. Perlu dicatat bahwa hanya menambahkan teks akselerator pada

metode setText() tidak membuat tombol akselerator otomatis ditambahkan dalam

metode setAccelerator(). Menambahkan teks akselerator pada setText() berfungsi

untuk membetulkan kesalahan yang terjadi jika tombol akselerator tidak terdapat pada

platform yang dituju. Misalnya pada contoh di atas, tombol <Ctrl> tidak ada pada

Macintosh, akan tetapi SWT akan mengganti tampilan Ctrl-S menjadi -S.

Tombol Sistem Jendela

Tombol yang diproses oleh sistem jendela tidak akan sampai pada aplikasi kita. Misalnya,

tombol Alt-F4 pada beberapa sistem akan menutup jendela yang aktid. Manager jendela

akan melakukan aksi terlebih dahulu dan tombol tersebut akan dikonsumsi oleh manager

jendela. Manager jendela tidak sama dengan sistem operasi. Pada Windows, manager

jendela itu terintegrasi dengan sistem operasi. Pada Linux misalnya manager jendela bisa

bermacam-macam, misalnya KDE dan Gnome.

Tombol-tombol mana yang akan dimanage oleh manager jendela berbeda-beda, tergantung

dari sistem jendela tersebut. Karena tidak adanya kesamaan pada setiap manager jendela,

maka kita tidak bisa mencegat penekenan tombol ini misalnya dialihkan untuk melakukan

fungsi lain.

Page 412: Java With Eclipse

<!-- @page { margin: 0.79in } TD P { margin-bottom: 0in } P { margin-bottom: 0.08in } --

>

Widget-widget SWT

Pada bagian ini akan kita bahas widget-widget yang disediakan oleh SWT. Kelas Widget

merepresentasikan semua objek user interface. Kelas ini adalah kelas abstrak yang tidak

bisa langsung diinstansiasi.

Berikut ini adalah metode-metode yang dimiliki oleh seluruh widget turunan dari kelas

abstrak Widget yang mencakup hampir seluruh widget SWT.

Metode Penjelasan

dispose() Menghapus widget dari memori.

isDisposed() Mengembalikan true jika widget ini sudah dihapus dari memori

setData(Object o)

Menambahkan data tambahan (terserah kita) ke dalam suatu

widget. Data ini disebut data aplikasi. Data ini tidak akan

ditampilkan pada widget.

getData()Mengembalikan Object yang merupakan data yang bisa kita

tambahkan ke dalam widget

setData(String

key, Object o)

Jika kita akan menambahkan banyak data ke dalam objek, akan

lebih mudah jika kita menandai data tersebut dengan suatu kunci

key. Data ini tidak akan ditampilkan pada widget.

getData(String

key)

Mengembalikan Object yang merupakan data tambahan ke suatu

widget, yang memiliki kunci key

getStyle() Mengembalikan int yang merupakan bit gaya suatu widget.

Event ini terdapat pada semua kelas turunan dari kelas abstrak Widget

Kelas Event

(event

bertipe)

Interface/Kelas

Listener

(listener

bertipe)

Metode (listener bertipe)

Jenis event

(event

tanpa tipe)

Penjelasa

n

DisposeEven

t

DisposeListene

r

widgetDisposed(DisposeEven

t)

SWT.Dispos

e

Widget

dihapus

Page 413: Java With Eclipse

Shell

Objek bertipe Shell adalah suatu jendela pada aplikasi. Objek yang tidak memiliki induk

(atau induknya bertipe Display) disebut shell tingkat paling tinggi. Objek yang memiliki

induk yang berupa shell lain disebut shell sekunder atau shell dialog.

Bit Gaya Shell

Shell memiliki beberapa bit gaya yang dibagi menjadi 3 jenis, sebagai berikut. Akan tetapi

perlu diingat bahwa bit gaya untuk shell hanya digunakan sebagai bantuan saja. Apa yang

digambar oleh manager jendela setiap sistem operasi bisa berbeda-beda.

1. Gaya yang mempengaruhi tampilan, suatu jendela bisa memiliki satu atau

beberapa bit gaya berikut

SWT.BORDER, suatu jendela memiliki garis tepi di sekitarnya

SWT.NO_TRIM, suatu jendela tanpa garis tepi (bisa digunakan untuk tool tip), akan

tetapi tidak bisa digabungkan dengan SWT.BORDER

SWT.CLOSE, suatu jendela memiliki tombol Close (atau biasanya sering digambar

dengan X) di pojok kanan atas jendela

SWT.MIN, suatu jendela memiliki tombol Minimize (biasanya sering digambar

dengan -) di pojok kanan atas jendela

SWT.MAX, suatu jendela memiliki tombol Maximimze di pojok kanan atas jendela

SWT.RESIZE, suatu jendela memiliki tombol Resize di pojok kanan atas jendela

SWT.TITLE, suatu jendela memiliki judul.

SWT.ON_TOP, suatu jendela yang akan berada di atas jendela-jendela lain bahkan

jendela dari aplikasi lain. Jendela ini tidak memblok input ke jendela lain, artinya

hanya tampilannya saja yang berada paling atas. Bisa digunakan untuk

menampilkan logo pada saat program pertama kali dijalankan.

SWT.TOOL, suatu jendela yang berbentuk seperti tips sesaat yang tujuannya

memberikan bantuan kepada user. Dalam Eclipse, biasanya toop tips ini muncul

ketika kita mengarahkan mouse ke suatu perintah, kemudian berhenti sesaat di

sana. Kemudian akan muncul bantuan tentang suatu perintah tersebut, yang

menjelaskan di antaranya input dan output suatu metode dan komentar bagaimana

menggunakan suatu metode.

2. Bit gaya yang mempengaruhi modalitas suatu jendela

Modalitas adalah bagaimana suatu jendela mempertahankan fokus input pada dirinya dan

memblokade input ke jendela lain. Modalitas suatu jendela tergantung pada siapa yang bisa

Page 414: Java With Eclipse

diblokade inputnya. Hanya salah satu modalitas saja yang bisa diberikan kepada suatu

jendela

SWT.MODELESS, suatu jendela tidak memblok jendela lain, biasanya merupakan

default dari suatu jendela shell tingkat atas yang baru dibuat

SWT.PRIMARY_MODAL, suatu jendela memblok input induknya, biasanya digunakan

untuk kotak dialog

SWT.APPLICATION_MODAL, suatu jendela memblok input jendela-jendela lain pada

aplikasi yang sama

SWT.SYSTEM_MODAL, suatu jendela memblok seluruh jendela bahkan jendela

aplikasi lain.

3. Bit gaya untuk kenyamanan pemrograman

SWT memberikan beberapa bit gaya yang bisa digunakan seorang programmer yang

merupakan gabungan dari beberapa bit gaya di atas, yang merupakan kombinasi yang

sering digunakan dalam aplikasi. Oleh karena itu bit gaya ini digunakan untuk kenyamanan

programmer akan tetapi memiliki efek yang persis sama dengan apabila kita

menggabungkannya sendiri

SWT.SHELL_TRIM, merupakan gaya shell yang biasa digunakan untuk jendela suatu

aplikasi. Merupakan gabungan dari (SWT.CLOSE | SWT.TITLE | SWT.MIN | SWT.MAX |

SWT.RESIZE)

SWT.DIALOG_TRIM, merupakan gaya shell yang biasa digunakan untuk kotak

dialog. Merupakan gabungan dari (SWT.CLOSE | SWT.TITLE | SWT.BORDER)

Untuk melihat efek dari beberapa bit gaya di atas, lakukan ekspremen kecil pada program

berikut :

package com.lyracc.contohshell;

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

public class ContohShell {

/**

* @param args

*/

public static void main(String[] args) {

Display display = new Display();

Shell shell = new Shell(display, SWT.CLOSE | SWT.BORDER);

Page 415: Java With Eclipse

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

}

Anda bisa mengganti-ganti kontruktor shell dengan beberapa kombinasi berikut dan lihat

hasilnya sendiri :

Shell shell = new Shell(display, SWT.SHELL_TRIM)

Shell shell = new Shell(display, SWT.DIALOG_TRIM)

Shell shell = new Shell(display, SWT.TOOL)

Shell shell = new Shell(display, SWT.CLOSE | SWT.BORDER)

Shell shell = new Shell(display, SWT.NO_TRIM)

Beberapa contoh di atas tidak akan menampilkan tombol close, sehingga untuk

mematikannya Anda harus menghentikannya dari dalam Eclipse dengan menekan tombol

Contoh berikut (kode lengkapnya untuk diimport ke dalam Eclipse bisa diunduh di sini) ini

membuat shell kosong, jika mouse kita berhenti di suatu tempat (hover), shell baru yang

berisi tool tip akan ditampilkan, seperti pada gambar berikut. Tool tip ini akan hilang jika

mouse berpindah tempat.

Untuk membuatnya, kita akan membuat satu kelas yang khusus menangani event, yang

kita namakan ToolTipListener. Kelas ini mengimplementasikan MouseMoveListener

dan MouseTrackListener (lihat bagian penanganan mouse untuk apa apa listener

digunakan). MouseTrackListener digunakan untuk menangkap event hover yang akan

membuat shell baru, kemudian menambahkan label untuk menampilkan tulisan ToolTip.

Shell baru ini kita buat dengan bit gaya SWT.TOOL. Berikut ini adalah listing dari kelas

ToolTipListener.

package com.lyracc.tooltipdemo;

import org.eclipse.swt.*;

Page 416: Java With Eclipse

import org.eclipse.swt.events.*;

import org.eclipse.swt.widgets.*;

import org.eclipse.swt.layout.*;

import org.eclipse.swt.graphics.*;

public class ToolTipListener implements MouseTrackListener,

MouseMoveListener {

Shell tip = null;

Label label = null;

// Metode buatToolTip dipanggil oleh event hover

void buatToolTip(MouseEvent e) {

// Ambil display dan shell utama dari event

Display displayUtama = e.display;

Shell shellUtama = displayUtama.getActiveShell();

// Buat tool tip baru sebagai shell, dengan bit gaya SWT.ON_TOP

tip = new Shell(shellUtama, SWT.ON_TOP);

tip.setLayout(new FillLayout());

// Tambahkan label

label = new Label(tip, SWT.NONE);

label.setText("ToolTip");

// Tambahkan tip ke dalam shell supaya bisa digambar

tip.pack();

}

// Metode tutupToolTip dipanggil ketika mouse pindah

void tutupToolTip() {

if (tip != null) tip.dispose();

tip = null;

}

@Override

public void mouseEnter(MouseEvent e) {

}

@Override

public void mouseExit(MouseEvent e) {

Page 417: Java With Eclipse

// Jika mouse keluar dari shell, tutup tool tip

tutupToolTip();

}

@Override

public void mouseHover(MouseEvent e) {

// Jika tip sudah ada, keluar dan tidak lakukan apa-apa

if (tip != null) return;

// Jika belum ada, buat tool tip baru

buatToolTip(e);

// Mengambil display dan shell utama dari event yang memanggil

Display displayUtama = e.display;

Shell shellUtama = displayUtama.getActiveShell();

// Hitung di mana tool tip akan ditampilkan, diambil dari lokasi

mouse

// yang disimpan di dalam e.x dan e.y

Rectangle rect = tip.getBounds();

rect.x = e.x;

rect.y = e.y + 22;

// Ubah posisi tip menggunakan metode setbounds

tip.setBounds(

displayUtama.map(shellUtama, null, rect));

// Buat supaya tip bisa dilihat (visible)

tip.setVisible(true);

}

@Override

public void mouseMove(MouseEvent e) {

// Jika mouse pindah lokasi, tutup tool tip

tutupToolTip();

}

}

Kelas utamanya (yang memiliki metode main()) akan menginstansiasi kelas

ToolTipListener dan menambahkan listener ke shell utamanya. Berikut ini adalah listing

kelasnya.

Page 418: Java With Eclipse

package com.lyracc.tooltipdemo;

import org.eclipse.swt.widgets.*;

import org.eclipse.swt.layout.*;

public class ToolTipDemo {

/**

* @param args

*/

public static void main(String[] args) {

final Display display = new Display();

final Shell shell = new Shell(display);

shell.setLayout(new FillLayout());

// Membuat instansi listener baru

ToolTipListener tipListener = new ToolTipListener();

// Tambahkan listener ke dalam shell

// (kelas listener kita mengimplementasi kelas MouseMoveListener

// dan kelas MouseTrackListener, kita tambahkan listener tersebut

// sendiri-sendiri)

shell.addMouseMoveListener(tipListener);

shell.addMouseTrackListener(tipListener);

shell.pack();

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

}

Metode pada Shell

Mengeset judul jendela dan icon jendela

Metode Penjelasan

setText(String Mengeset judul jendela. Tidak bisa mengandung mnemonik.

Page 419: Java With Eclipse

judul)

getText()Mengambil judul jendela. Jika tidak pernah diset, maka hasilnya

adalah string kosong

setImage(Image

gambar)

Mengeset gambar icon pada jendela, atau gambar icon yang

akan digunakan ketika jendela diminimasi

getImage() Mengambil gambar icon jendela

setImages(Image[]

gambar)

Mengeset beberapa gambar icon pada jendela. Pada kebanyakan

manager jendela, hanya gambar terakhir saja yang ditampilkan

getImages() MEngambil gambar-gambar yang sudah diset pada jendela

Membuka dan menutup jendela

Metode Penjelasan

open() Membuka jendela dan mengarahkan fokus pada jendela ini.

setActive()Membuat suatu jendela menjadi jendela aktif. Mirip dengan

open() akan tetapi fokus tidak otomatis di set ke jendela aktif

forceActive()Memaksa agar suatu jendela menjadi jendela aktif, akan tetapi

tidak selamanya akan berhasil.

close()Menutup jendela, sama seperti ketika user menutup jendela

dengan menekan tombol (X) di sudut kanan atas jendela.

isEnabled()Mengembalikan true jika jendela ini bisa menerima fokus, dan

semua induknya hingga level tertinggi juga enabled.

getEnabled() Mengembalikan true jika jendela ini bisa menerima fokus

setEnabled(boolean

enabled)Mengeset apakah suatu jendela bisa menerima fokus

isVisible() Mengembalikan true jika jendela bisa dilihat

getVisible() Mengembalikan true jika jendela bisa dilihat

setVisible(boolean

visible)Mengeset apakah suatu jendela bisa dilhat atau tidak.

Mengubah ukuran suatu jendela

Metode Penjelasan

getBounds() Mengembalikan Rectangle yang merupakan koordinat

Page 420: Java With Eclipse

sudut-sudut suatu jendela relatif terhadap induknya

setRegion(Region

wilayah)

Mengeset bentuk suatu jendela, misalnya kita ingin jendela

kita berbentuk bentuk lain selain persegi panjang.

getRegion()

Mengembalikan Region yang merupakan bentuk suatu

jendela (bisa jadi bukan berupa persegi panjang, tapi bentuk

lain yang kita definisikan sebelumnya). Mengembalikan

null jika bentuk jendela seperti biasa (persegi panjang)

getSize()Mengembalikan Point yang merupakan ukuran suatu

jendela, x adalah lebar dan y adalah tingginya

setSize(int lebar, int

tinggi)Mengeset ukuran suatu jendela

setSize(Point p)Mengeset ukuran suatu jendela, dengan parameter dalam

bentuk Point

getMinimumSize()

Mengembalikan Point ukuran minimum suatu jendela.

Ukuran minimum adalah ukuran di mana user tidak bisa

memperkecil ukuran jendela lagi lebih kecil dari ukuran ini.

setMinimumSize(int

lebar, int tinggi)

Mengeset ukuran minimum suatu jendela. User tidak bisa

memperkecil jendela lebih kecil dari ukuran ini.

setMinimumSize(Point p)Mengeset ukuran minimum suatu jendela. Menerima

argumen bertipe Point

getLocation() Mengambil lokasi suatu jendela relatif terhadap induknya

setLocation(int x, int

y)Mengeset lokasi suatu jendela relatif terhadap induknya

getFullScreen() Mengembalikan true jika jendela dalam keadaan full screen

setFullScreen(boolean

fullscreen)Mengeset keadaan full screen suatu jendela

getMaximized()Mengembalikan true jika jendela dalam keadaan

dimaksimasi (masiximized)

setMaximized(boolean

maximized)Mengeset keadaan maximized suatu jendela

getMinimized() Mengembalikan true jika jendela dalam keadaan

Page 421: Java With Eclipse

diminimasi (minimized)

setMinimized(boolean

minimized)Mengeset keadaan minimasi suatu jendela

Mengelola isi suatu jendela

Metode Penjelasan

setMenuBar(Menu menu) Mengeset menu suatu jendela

getMenuBar() Mengembalikan Menu suatu jendela

getShells()Mengembalikan array Shell[] yang merupakan shell-

shell anak dari shell ini

getChildren()Mengembalikan array Control[] yang merupakan

wkontrol-kontrol yang terdapat di dalam jendela ini.

getDefaultButton()Mengembalikan tombol apa yang akan dieksekusi jika

tombol <Enter> ditekan (berguna untuk kotak dialog)

setDefaultButton(Button

tombol)

Mengeset tombol apa yang akan dieksekusi jika tombol

<Enter> ditekan (biasanya pada kotak dialog)

getBackground()Mengembalikan Color yang merupakan latar belakang

suatu jendela

setBackground(Color warna) Mengeset warna latar belakang suatu jendela

getForeground()Mengembalikan Color yang merupakan latar depan

suatu jendela

setForeground(Color warna) Mengeset warna latar depan suatu jendela

getBackgroundImage()Mengembalikan Image yang merupakan gambar latar

belakang suatu jendela

setBackgroundImage(Image

gambar)

Mengeset gambar latar belakang suatu jendela. Jika

gambar lebih kecil dari ukuran jendela, gambar akan

dibuat berulang-ulang.

Pada contoh berikut ini kita akan membuat shell berbentuk oval dengan latar belakang

merah. Kita bisa memindahkan shell tersebut dengan menggesernya ke posisi lain (tekan

mouse di dalam oval, kemudian gerakkan mouse). Karena tidak ada tombol (X) di sudut

Page 422: Java With Eclipse

kanan atas jendela, kita tambahkan event yang akan menutup jendela ketika sembarang

tombol keyboard ditekan.

Program lengkapnya dapat diunduh di sini dan dapat Anda import ke dalam Eclipse.

Berikut program lengkapnya.

package com.lyracc.shellberbentuklingkaran;

import org.eclipse.swt.*;

import org.eclipse.swt.graphics.*;

import org.eclipse.swt.widgets.*;

public class ShellBerbentukLingkaran {

// Jumlah titik pada lingkaran, semakin banyak semakin halus

// Dari rumus geometri, untuk membuat suatu bangun datar

// dibutuhkan minimum 3 titik.

final static int JML_TITIK = 151;

/**

* @param args

*/

public static void main(String[] args) {

Display display = new Display();

// Membuat shell baru tanpa garis tepi

Shell shell = new Shell(display, SWT.NO_TRIM);

// Mengambil warna hitam dari sistem

Color warnaMerah =

display.getSystemColor(SWT.COLOR_RED);

// Mengeset warna dan ukuran shell mula-mula

shell.setBackground(warnaMerah);

Page 423: Java With Eclipse

shell.setSize(100, 200);

// Mengambil batas-batas shell

Rectangle wilayah = shell.getClientArea();

// Koordinat tengah jendela

Point tengah = new Point(0, 0);

tengah.x = wilayah.x + wilayah.width / 2;

tengah.y = wilayah.y + wilayah.height / 2;

int[] koordinat = new int[JML_TITIK * 2];

// menggunakan rumus trigonometri untuk mendapatkan koordinat

lingkaran

for (int i = 0; i < JML_TITIK; ++i) {

double r = Math.PI * 2 * i / JML_TITIK;

koordinat[i*2] = (int)(tengah.x + Math.cos(r) * wilayah.width

/ 2);

koordinat[i*2+1] = (int)(tengah.y + Math.sin(r) *

wilayah.height / 2);

}

// Membuat region baru dan menambahkannya pada shell

Region region = new Region(display);

region.add(koordinat);

shell.setRegion(region);

Listener listener = new Listener() {

int offsetX = 0;

int offsetY = 0;

public void handleEvent(Event e) {

Shell shellUtama = (Shell)e.widget;

switch (e.type) {

// Jika mouse ditekan ambil koordinat saat itu

case SWT.MouseDown:

if (e.button == 1) {

offsetX = e.x;

offsetY = e.y;

Page 424: Java With Eclipse

}

break;

case SWT.MouseMove:

// Jika mouse pindah sambil ditekan dengan tombol

kiri

if ((e.stateMask & SWT.BUTTON1) != 0) {

// Ambil lokasi mouse relatif terhadap

display

Point pt = shellUtama.toDisplay(e.x, e.y);

// Translasikan dengan offset awal

pt.x -= offsetX;

pt.y -= offsetY;

// Set lokasi baru

shellUtama.setLocation(pt);

}

break;

case SWT.KeyDown :

// Jika tombol keyboard ditekan tutup jendela

shellUtama.dispose();

break;

}

}

};

// Tambah listener ke dalam shell

shell.addListener(SWT.MouseDown, listener);

shell.addListener(SWT.MouseMove, listener);

shell.addListener(SWT.KeyDown, listener);

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

// Jangan lupa untuk mendispose region juga

region.dispose();

display.dispose();

Page 425: Java With Eclipse

}

}

Event pada Shell

Event berikut hanya terdapat pada widget Shell.

Kelas

Event

(event

bertipe)

Interface/Kelas

Listener

(listener

bertipe)

Metode (listener bertipe)

Jenis event

(event tanpa

tipe)

Penjelasan

shellActivated(ShellEvent) SWT.ActivateJendela

diaktifkan

shellClosed(ShellEvent) SWT.Close

Jendela Shell

ditutup

(dengan

tombol X)

shellDeactivated(ShellEvent) SWT.DeactivateJendela

dinonaktifkan

shellIconified(ShellEvent) SWT.IconifyJendela Shell

diminimasi

ShellEvent

ShellListener

(dan

ShellAdapter)

shellDeiconified(ShellEvent) SWT.Deiconify

Jendela Shell

dibuka

(restore)

Untuk menambahkan event bertipe pada suatu shell, gunakan metode

addShellListener(ShellListener listener).

Berikut ini adalah contoh program untuk memberikan kotak dialog yang menanyakan

apakah kotak dialog akan ditutup, kemudian menampilkan tombol "Yes/No/Cancel".

Program ini menangkap event SWT.Close yang dilempar ketika program hendak ditutup.

Jika jawaban user "Cancel", maka variabel doit di dalam event akan diisi dengan false,

yang berarti membatalkan event menutup program.

Page 426: Java With Eclipse

Berikut ini adalah listing program lengkap, yang juga dapat diunduh di sini untuk diimport

ke dalam Eclipse.

package com.lyracc.tutupshell;

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

import org.eclipse.swt.events.*;

public class TutupShell {

/**

* @param args

*/

public static void main(String[] args) {

Display display = new Display();

Shell shell = new Shell(display);

// Mengubah ukuran jendela dan mengeset judul jendela

shell.setSize(200, 200);

shell.setText("Tutup Shell");

// Membuat event listener shell menggunakan listener bertipe

ShellAdapter listener = new ShellAdapter() {

@Override

// Kita tindih metode shellClosed untuk mencegat event

penutupan shell

public void shellClosed(ShellEvent e) {

// Ambil shell dari mana event ini dikirim

Shell induk = (Shell)e.widget;

// Buat message box yang dengan modalitas aplikasi

// dan tombol yes, no, cancel

Page 427: Java With Eclipse

MessageBox box = new MessageBox(induk, SWT.YES | SWT.NO |

SWT.CANCEL | SWT.APPLICATION_MODAL);

// Set judul kotak dialog dengan judul yang sama dengan

judul jendela

box.setText(induk.getText());

// Set pesan yang akan ditampilkan

box.setMessage("Simpan hasil kerjaan Anda?");

// Buka pesan dialog, jika hasilnya "Cancel" batalkan

penutupan

// kotak dialog dengan mengeset doit = false;

// Jawaban lainnya (yes dan no) akan meneruskan penutupan

aplikasi

switch (box.open()) {

case SWT.YES: break;

case SWT.NO: break;

case SWT.CANCEL :

e.doit = false;

break;

}

}

};

// Tambah listener pada shell utama

shell.addShellListener(listener);

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

}

Kotak Dialog

Kelas Dialog adalah kelas super abstrak dari beberapa jenis kotak dialog yang merupakan

kotak dialog bawaan dari sistem operasi. Kotak dialog adalah shell tingkat tinggi yang

digunakan oleh sistem operasi untuk memasukkan suatu nilai atau memberi tahu user

tentang suatu suatu pesan atau kesalahan. Misalnya, kotak dialog file akan meminta user

Page 428: Java With Eclipse

memilih suatu file yang kemudian digunakan untuk programmer untuk membuka dan

membaca isi file tersebut.

Kotak dialog sangat berbeda dari satu platform ke platofrm yang lain. Pada beberapa

platform, programmer bisa mengakses widget-widget di dalamnya, sedangkan pada

platform lain hal ini tidak dimungkinkan. Dengan alasan ini, SWT dianggap sebagai kotak

hitam. Lebih penting lagi, kotak dialog bukan widget. Dialog bukan kelas turunan dari

kelas Widget dan tidak memiliki metode yang dimiliki oleh widget-widget lain.

Ada beberapa jenis kotak dialog yang disediakan SWT :

Jenis kotak

dialogPenjelasan

MessageBoxKotak dialog untuk memberi pesan atau pertanyaan sederhana, seperti

Yes/No/Cancel/Ok

ColorDialog Kotak dialog untuk memilih warna

DirectoryDialog Kotak dialog untuk memilih direktori

FileDialog Kotak dialog untuk memilih file

FontDialog Kotak dialog untuk memilih huruf

PrintDialog Kotak dialog untuk melakukan pencetakan ke printer

Ada 3 jenis modalitas yang didukung oleh suatu kotak dialog, yang bisa diset sebagai bit

gaya ketika membuat kotak dialog :

SWT.PRIMARY_MODAL, suatu kotak dialog memblok input induknya, default untuk

kotak dialog

SWT.APPLICATION_MODAL, suatu kotak dialog memblok input jendela-jendela lain

pada aplikasi yang sama

SWT.SYSTEM_MODAL, suatu kotak dialog memblok seluruh jendela bahkan jendela

aplikasi lain.

Untuk membuat kotak dialog, sama seperti pada jendela, yaitu dengan menggunakan

konstruktor

new Dialog(Shell induk);

atau

new Dialog(Shell induk, int gaya);

Gaya yang bisa digunakan berbeda-beda tergantung dari jenis kotak dialognya.

Page 429: Java With Eclipse

Beberapa metode umum yang tersedia untuk semua jenis kotak dialog adalah sebagai

berikut

Metode Penjelasan

getParent()Mengembalikan Shell yang merupakan induk dari kotak dialog

ini

getStyle()Mengembalikan int yang merupakan kumpulan gaya dari kotak

dialog ini

getText() Mengembalikan String yang merupakan judul kotak dialog

setText(String

judul)Mengeset judul kotak dialog

Kotak Dialog MessageBox

Kotak dialog bertipe MessageBox digunakan untuk memberikan pesan kepada user dan

meminta input sederhana dari user. Input yang diminta ini diberikan sebagai bit gaya ketika

suatu kotak dialog dibuat.

Berikut ini adalah beberapa bit gaya dari suatu MessageBox :

Salah satu dari SWT.ICON_ERROR, SWT.ICON_INFORMATION, SWT.ICON_QUESTION,

SWT.ICON_WARNING, SWT.ICON_WORKING, yang memberikan tambahan icon sebagai

penanda jenis kotak dialog misalnya apakah kotak dialog ini merupakan kotak

dialog kesalahan, informasi, peringatan, atau pertanyaan. Misalnya, pada Windows

Vista bit-bit gaya tersebut menampilkan icon-icon seperti berikut

Gaya Gambar

SWT.ICON_ERROR

SWT.ICON_INFORMATION

SWT.ICON_QUESTION

SWT.ICON_WARNING

SWT.ICON_WORKING

Page 430: Java With Eclipse

Bit gaya yang merupakan pertanyaan yang diberikan kepada user. Ada beberapa

kombinasi yang bisa digunakan, seperti pada tabel berikut, beserta contohnya pada

Windows Vista.

Gaya Tampilan

SWT.OK

SWT.OK | SWT.CANCEL

SWT.YES | SWT.NO

SWT.YES | SWT.NO | SWT.CANCEL

SWT.RETRY | SWT.CANCEL

SWT.ABORT | SWT.RETRY | SWT.IGNORE

Metode-metode pada Message Box

Berikut ini adalah beberapa metode yang tersedia pada message box

Metode Penjelasan

setMessage(String

pesan)

Mengeset pesan yang akan ditampilkan pada MessageBox. Pesan

ini akan ditampilkan sebelum tombol

Ok/Yes/No/Cancel/Retry/Abort/Ignore

String

getMessage()

Mengembalikan String yang merupakan pesan yang akan

ditampilkan pada MessageBox

int open()

1. Membuka kotak dialog untuk dipresentasikan kepada user

2. Mengembalikan int yang merupakan jawaban dari user.

int yang dikembalikan bisa dibandingkan dengan salah satu

dari SWT.YES, SWT.NO, SWT.CANCEL, SWT.ABORT,

SWT.RETRY, SWT.IGNORE untuk mengecek tombol apa yang

ditekan oleh user.

Untuk melihat contoh penggunaan MessageBox lihat contoh sebelumnya pada bahasan

tentang event pada shell.

Page 431: Java With Eclipse

Kotak Dialog FileDialog

Kelas FileDialog melambangkan kotak dialog untuk memilih file. Pada dasarnya ada dua

jenis kotak dialog pemilihan file, yaitu untuk menyimpan dan membaca file, seperti

diimplementasikan pada kebanyakan program File -> Open dan File -> Save. Keduanya

hampir sama dalam tampilan, kecuali beberapa detail lain yang berbeda, misalnya tombol

"Open" pada kotak dialog untuk membaca file, dan "Save" untuk kotak dialog untuk

menyimpan file.

Kotak dialog pemilihan file bisa diset untuk memilih hanya beberapa file saja dengan

bentuk tertentu, misalnya hanya file berekstensi *.html saja yang ditampilkan dalam daftar

file. Ini disebut file filter. Kotak dialog pemiihan file juga bisa diset untuk memilih satu

file saja atau beberapa file sekaligus.

Mirip dengan kotak dialog lain, kotak dialog file tidak akan ditutup jika user sebelum user

menekan tombol "Open" atau "Save" atau "Cancel".

Berikut ini adalah beberapa bit gaya yang bisa diberikan ketika kotak dialog pemilihan file

dibuat dengan konstruktornya.

Page 432: Java With Eclipse

Bit gaya Penjelasan

SWT.OPEN Membuat kotak dialog untuk membuka file

SWT.CLOSE Membuat kotak dialog untuk menutup file

SWT.SINGLEMembuat kotak dialog pemilihan file yang hanya bisa memilih satu file saja

(perilaku standar jika tidak disebutkan)

SWT.MULTIMembuat kotak dialog pemilihan file yang bisa memilih beberapa file

sekaligus

Metode-metode pada FileDialog

Metode Penjelasan

open()

1. Membuka kotak dialog untuk dipresentasikan

kepada user

2. Mengembalikan string yang merupakan nama

lengkap (termasuk direktorinya) file yang

dipilih. Jika tidak ada file yang dipilih atau

tombol Cancel ditekan, akan mengembalikan

null. Jika kotak dialog diset dengan

SWT.MULTI, dan beberapa file dipilih, hanya

file pertama yang dikembalikan

getFileName()Mengembalikan String nama lengkap file yang

dipilih, atau string kosong jika tidak ada yang dipilih

getFileNames()

Mengembalikan array String[] yang berisi nama-

nama file yang dipilih. Bisa berisi array dengan

panjang 0 jika tidak ada file yang dipilih

setFileName(String namafile)

Mengeset nama file pada kotak dialog. Berguna untuk

memberi pentunjuk kepada user bahwa nama file

dengan nama tertentu saja yang sebaiknya dipilih

getFilterPath()Mengembalikan String path ketika kotak dialog

ditutup

setFilterPath(String path) Mengeset path di mana pencarian file dimulai

getFilterExtensions() Mengembalikan array String[] ekstensi file-file

Page 433: Java With Eclipse

yang akan ditampilkan pada kotak pencarian file

setFilterExtensions(String[]

ekstensi)

Mengeset ekstensi file-file yang akan ditampilkan

pada kotak pencarian file, misalnya "*.txt" untuk

menampilkan hanya file berekstensi txt saja.

getFilterNames()

Mengembalikan String[] nama dari ekstensi file.

Biasanya nama ini digunakan untuk memberikan

nama ekstensi yang lebih jelas. Misalnya "File teks"

untuk menjelaskan ekstensi "*.txt"

setFilterNames(String[]

namaEkstensi)

Mengeset penjelasan dari ekstensi file-file yang akan

ditampilkan.

getFilterIndex()Mengembalikan int yang berisi indeks ekstensi yang

dipilih user. Indeks dimulai dari 0

setFilterIndex(int indeks)

Mengeset filter mana yang akan ditunjukkan terlebih

dahulu kepada user. Jika tidak diset, maka ekstensi

yang pertama kali dimasukkan akan ditampilkan

terlebih dahulu

getOverwrite()

Mengembalikan true jika kotak dialog tambahan

untuk menimpa file akan ditampilkan yang berguna

untuk mencegah user menimpa data. Digunakan pada

kotak dialog bertipe SWT.SAVE

setOverwrite(boolean timpa)

Mengeset apakah kotak dialog tambahan untuk

menimpa file untuk ditampilkan pada kotak dialog

bertipe SWT.SAVE

Berikut ini (dapat diunduh di sini) adalah contoh untuk menampilkan kotak dialog untuk

menyimpan suatu file. Ketika Anda menjalankan program ini, shell kosong akan

ditampilkan. Jika Anda menekan tombol mouse di dalam shell kosong ini, kotak dialog

untuk menyimpan file akan ditampilkan. Hanya ada dua ekstensi yang diperbolehkan, yaitu

"*.java" dan "*.class". Jika nama file sudah ada, program akan menanyakan apakah Anda

akan menimpa isi file. Jika tidak, maka kotak dialog kembali ditampilkan. Jika ya, kotak

dialog akan ditutup, dan hasil pilihan Anda akan ditampilkan pada MessageBox yang

berisi nama file pilihan Anda.

package com.lyracc.kotakdialogfile;

Page 434: Java With Eclipse

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

import org.eclipse.swt.events.*;

public class KotakDialogFile {

/**

* @param args

*/

public static void main(String[] args) {

Display display = new Display();

Shell shell = new Shell(display);

// Mengubah ukuran jendela dan mengeset judul jendela

shell.setSize(200, 200);

shell.setText("Demo kotak dialog file");

// Membuat event listener shell menggunakan listener bertipe

MouseAdapter listener = new MouseAdapter() {

@Override

// Kita tindih metode mouseDown untuk menampilkan kotak

dialog

// ketika mouse di klik

public void mouseDown(MouseEvent e) {

// Ambil shell dari mana event ini dikirim

Shell induk = (Shell)e.widget;

// Buat kotak dialog file yang dengan modalitas aplikasi

FileDialog kotakDialogFile = new FileDialog(induk,

SWT.SAVE |

SWT.APPLICATION_MODAL);

// Set judul kotak dialog

kotakDialogFile.setText("Nama file untuk disimpan");

// Set filter untuk menyaring ekstensi

kotakDialogFile.setFilterExtensions(new String[] {

"*.java", "*.class"

}

);

// Set penjelasan filter

Page 435: Java With Eclipse

kotakDialogFile.setFilterNames(new String[] {

"Sumber kode Java (*.java)", "File biner Java

(*.class)"

}

);

// Buat kotak dialog untuk menanyakan user apakah ingin

menimpa

// isi file yang sudah ada.

kotakDialogFile.setOverwrite(true);

// Buka kotak dialog file dan ambil file yang dipilih

// dan letakkan di variabel namaFile

String namaFile = kotakDialogFile.open();

MessageBox pesan = new MessageBox(induk,

SWT.ICON_INFORMATION | SWT.OK);

pesan.setMessage("File yang dipilih " + namaFile);

pesan.open();

}

};

// Tambah listener pada shell utama

shell.addMouseListener(listener);

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

}

Kotak Dialog DirectoryDialog

Kelas DirectoryDialog melambangkan kotak dialog untuk memilih direktori. Kelas ini

lebih sederhana dibandingkan dengan FileDialog. Kelas ini tidak memiliki bit gaya

apapun.

Page 436: Java With Eclipse

Untuk membuat kotak dialog untuk direktori, cara yang sama bisa dilakukan seperti pada

FileDialog. Metode-metode yang dimiliki juga mirip dengan FileDialog dengan lebih

sedikit variasi, yaitu :

Metode Penjelasan

open()

1. Membuka kotak dialog untuk dipresentasikan kepada

user

2. Mengembalikan string yang merupakan nama lengkap

direktori yang dipilih. Jika tidak ada direktori yang

dipilih atau tombol Cancel ditekan, akan

mengembalikan null.

getFilterPath() Mengembalikan String path ketika kotak dialog ditutup

setFilterPath(String

path)Mengeset path di mana pencarian file dimulai

getMessage()

Mengembalikan String yang merupakan pesan yang

ditampilkan sebagai instruksi kepada user untuk memilih

direktori

getMessage(String

pesan)

Mengeset pesan yang ditampilkan sebagai instruksi kepada

user untuk memilih direktori

Page 437: Java With Eclipse

Kotak Dialog ColorDialog

Kotak dialog ColorDialog digunakan untuk memilih warna, misalnya digunakan untuk

memilih warna huruf, latar belakang kanvas, dan lain sebagainya.

Kotak dialog ini tidak memiliki bit gaya atau event. Keluarannya bertipe RGB yang

merupakan representasi dari gabungan warna Red Green Blue yang tidak bergantung pada

suatu device. Kelas RGB tersedia dalam paket org.eclipse.swt.graphics

Untuk menggunakannya sama seperti pada kotak-kotak dialog lainnya.

Berikut ini adalah beberapa metode yang terdapat pada kelas ColorDialog

Metode Penjelasan

open()

1. Membuka kotak dialog untuk dipresentasikan kepada user

2. Mengembalikan RGB yang merupakan warna yang dipilih user. Jika

tidak ada warna yang dipilih atau tombol Cancel ditekan, akan

mengembalikan null.

getRGB() Mengembalikan RGB warna yang dipilih user.

setRGB(RGB

warna)Mengeset warna RGB awal ketika kotak dialog dipresentasikan kepada user

Kotak Dialog FontDialogKotak

Page 438: Java With Eclipse

dialog FontDialog digunakan untuk memilih salah satu huruf dari huruf-huruf yang

tersedia pada suatu platform. Suatu huruf yang tersedia pada platform yang satu belum

tentu tersedia pada platform yang lain.

Sama seperti ColorDialog, kotak dialog ini tidak mempunyai bit gaya yang bisa diset.

Untuk menggunakannya mirip dengan kotak-kotak dialog yang lain.

Beberapa platform memungkinkan kita untuk mengeset jenis huruf dan warnanya

sekaligus. SWT juga menyediakan metode untuk mengambil warna dan jenis huruf yang

dipilih user sekaligus.

Kotak dialog FontDialog mengembalikan array objek bertipe FontData.

Berikut ini adalah beberapa metode yang tersedia pada FontDialog

Metode Penjelasan

open()

1. Membuka kotak dialog untuk dipresentasikan kepada

user

2. Mengembalikan FontData yang merupakan huruf

yang dipilih. Jika tidak ada huruf yang dipilih atau

tombol Cancel ditekan, akan mengembalikan null.

getFontList()Mengembalikan array FontData[] yang berupa kumpulan

font yang dipilih user.

setFontList(FontData

huruf)

Mengeset huruf yang menjadi default ketika kotak dialog

ditampilkan

getRGB() Mengembalikan warna huruf yang dipilih dalam bentuk RGB

setRGB(RGB warna)Mengeset warna huruf yang menjadi default ketika kotak

dialog ditampilkan

Berikut ini adalah contoh menggunakan FontDialog. Shell yang berisi "contoh teks" akan

berubah warna dan penampilannya apabila kita mengubah jenis hurufnya dengan menekan

tombol "Ubah huruf".

Page 439: Java With Eclipse

Listing program lengkapnya adalah sebagai berikut, atau bisa juga diunduh di sini untuk

diimport ke dalam Eclipse.

package com.lyracc.kotakdialogfont;

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

import org.eclipse.swt.events.*;

import org.eclipse.swt.graphics.*;

import org.eclipse.swt.layout.*;

public class KotakDialogFont {

Page 440: Java With Eclipse

/**

* @param args

*/

public static void main(String[] args) {

Display display = new Display();

Shell shell = new Shell(display);

// Mengeset layout shell

shell.setLayout(new RowLayout());

// Mengubah ukuran jendela dan mengeset judul jendela

shell.setText("Demo kotak dialog huruf");

// Membuat teks baru

final Text teks = new Text(shell,SWT.BORDER | SWT.H_SCROLL |

SWT.V_SCROLL);

teks.setLayoutData(new RowData(100, 200));

teks.setText("contoh teks");

// Membuat tombol baru

Button tombol = new Button(shell, SWT.PUSH);

tombol.setText("Ubah huruf");

// Membuat kotak dialog font baru

final FontDialog dialog = new FontDialog(shell);

dialog.setText("Pilih huruf");

// Membuat event listener menggunakan listener bertipe

SelectionAdapter listener = new SelectionAdapter() {

@Override

// Ketika tombol ditekan

public void widgetSelected(SelectionEvent e) {

// Jika kotak dialog mengembalikan null, artinya

// tidak ada huruf yang dipilih

if (dialog.open() == null) return;

// Referensi ke display

Display displayUtama = e.display;

// Mengambil huruf yang dipilih

FontData[] list = dialog.getFontList();

Page 441: Java With Eclipse

// Ubah dari FontData ke Font

Font huruf = new Font(displayUtama, list);

// Set huruf pada teks

teks.setFont(huruf);

// Jangan lupa hapus dari memori untuk mencegah kebocoran

memori

huruf.dispose();

// Ambil warna dari dialog. Jika tidak null, ubah warna

teks

RGB rgb = dialog.getRGB();

if (rgb != null) {

Color warna = new Color(displayUtama, rgb);

teks.setForeground(warna);

warna.dispose();

}

}

};

// Tambah listener pada tombol

tombol.addSelectionListener(listener);

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

}

Dasar-dasar Kontrol dan Label

Kelas Control merepresentasikan elemen user interface yang berada di dalam suatu

jendela. Kelas ini merupakan kelas abstrak yang tidak bisa digunakan secara langsung.

Sedangkan Label, Button, Text, dan kontrol-kontrol lain yang biasa kita temui pada suatu

jendela merupakan kelas turunan dari kelas Control yang bisa kita gunakan. Kelas Shell

pun adalah salah satu kelas turunan tidak langsung dari kelas Control, sehingga beberapa

metode dan event pada kelas Control tersedia pada kelas Shell.

Kelas Control memiliki beberapa bit gaya, yaitu

Page 442: Java With Eclipse

Bit gaya Keterangan

SWT.NONE Bit gaya kosong, tidak ada gaya apa-apa

SWT.BORDERMembuat label yang diberi garis tepi (border). Bit gaya SWT.BORDER

dimiliki oleh semua Widget, bukan hanya Label

SWT.LEFT_TO_RIGHTTeks akan ditampilkan dari kiri ke kanan seperti bahasa Indonesia,

Inggris atau Latin lainnya

SWT.RIGHT_TO_LEFTTeks akan ditampilkan dari kanan ke kiri seperti bahasa Arab dan

Hebrew

Untuk lebih menjelaskan tentang kelas abstrak Control dan berbagai fiturnya mari kita

lihat kelas turunan yang paling sederhana, yaitu Label.

Label

Kelas Label adalah teks atau gambar yang tidak bisa dipilih oleh user dan tidak

menghasilkan event apa-apa. Label biasanya digunakan untuk menampilkan teks pasif,

misalnya judul suatu input teks atau keterangan lainnya.

Kelas Label memiliki beberapa bit gaya, yaitu

Bit gaya Keterangan

SWT.SEPARATORMembuat garis, baik horizontal atau vertikal, yang biasanya digunakan

sebagai pembatas antar label atau pembatas antar widget

SWT.SHADOW_IN

SWT.SHADOW_OUT

SWT.SHADOW_NONE

Hanya salah satu dari gaya ini yang boleh digunakan, hanya digunakan

dengan SWT.SEPARATOR

SWT.LEFT

SWT.RIGHT

SWT.CENTER

Hanya salah satu dari gaya ini yang boleh digunakan, hanya berguna

untuk teks atau gambar

SWT.WRAP Memotong teks jika tampilan tidak cukup besar

Mari kita buat contoh sederhana, yaitu menggambar satu jendela (shell) dan satu label yang

bertuliskan "Selamat Datang!". Kita bisa buat programnya seperti ini.

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

public class HelloSWT2 {

Page 443: Java With Eclipse

// Variabel-variabel widget kita deklarasikan sebagai

// variabel privat

private Display display;

private Shell shell;

private Label label1;

// Konstruktor kelas ini, untuk menginisialisasi semua widget

HelloSWT2() {

// Membuat display dan shell baru

display = new Display();

shell = new Shell(display);

shell.setSize(200, 200);

shell.setText("Hello SWT");

// Inisialisasi label

label1 = new Label(shell, SWT.LEFT);

label1.setText("Selamat Datang!");

}

// Perintah "standar" SWT, harus ada pada setiap aplikasi SWT

public void run() {

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

/**

* @param args

*/

public static void main(String[] args) {

// Instansiasi kelas ini, kemudian jalankan run

HelloSWT2 hello = new HelloSWT2();

hello.run();

}

}

Apa yang Anda dapatkan? Anda terkejut karena label tidak ditampilkan? Apa yang kurang

dari program kita di atas?

Page 444: Java With Eclipse

Ketika suatu kontrol pertama kali dibuat (label adalah kelas turunan dari kontrol, jadi label

juga berperilaku seperti kontrol), ukuran dan lokasinya adalah 0. Tugas kita sebagai

programmer untuk menentukan ukuran kontrol dan lokasi di mana ia akan diletakkan.

Bound (Tepi)

Suatu persegi panjang yang menjelaskan lokasi dan ukuran suatu kontrol pada induknya

disebut tepi suatu kontrol. Tepi suatu kontrol memiliki satuan piksel. Sedangkan unitnya

relatif terhadap induknya. Untuk shell utama, koordinatnya mengikuti koordinat layar.

Untuk kontrol yang berada di dalam shell, koordinat (0,0) adalah koordinat pojok kiri atas

dari shell. Jika suatu widget berada dalam widget lain, maka koordinat (0,0)-nya adalah

koordinat pojok kiri atas dari widget induknya.

Untuk mengeset dan mengambil lokasi dan ukuran suatu widget, kita bisa gunakan

beberapa metode berikut ini :

Metode Penjelasan

setBounds(int x, int y, int

lebar, int tinggi)Mengeset lokasi dan ukuran suatu kontrol

setBounds(Rectangle bound)Mengeset lokasi dan ukuran suatu kontrol.

Parameternya bertipe Rectangle

setLocation(int x, int y)Mengeset lokasi suatu kontrol relatif terhadap

induknya

setLocation(Point koordinat)Mengeset lokasi suatu kontrol relatif terhadap

induknya. Parameternya bertipe Point

setSize(int lebar, int tinggi) Mengeset ukuran suatu kontrol.

setSize(Point ukuran)Mengeset ukuran suatu kontrol. Parameternya

bertipe Point

Page 445: Java With Eclipse

getBounds()

Mengembalikan Rectangle yang merupakan

ukuran dan lokasi suatu kontrol relatif terhadap

induknya

getLocation()Mengembalikan Point yang merupakan lokasi

suatu kontrol relatif terhadap induknya

getSize()Mengembalikan Point yang merupakan ukuran

suatu kontrol

Perhatikan bahwa beberapa metode menerima dan mengembalikan Rectangle dan Point

sebagai parameter atau tipe keluarannya. Kelas Rectangle dan Point adalah kelas generik

yang dimiliki SWT untuk melambangkan persegi panjang dan suatu titik. Kelas-kelas ini

didefinisikan dalam paket org.eclipse.swt.graphics.

Kelas Rectangle memiliki variabel instansi x, y, height, dan width yang semuanya

bertipe int. Sedangkan kelas Point memiliki variabel instansi x dan y yang bertipe int.

Mari kita tengok kembali program di atas. Kita akan ubah sedikit yaitu memberi ukuran

dan lokasi label sehingga bisa ditampilkan dengan benar.

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

public class HelloSWT2 {

// Variabel-variabel widget kita deklarasikan sebagai

// variabel privat

private Display display;

private Shell shell;

private Label label1;

// Konstruktor kelas ini, untuk menginisialisasi semua widget

HelloSWT2() {

// Membuat display dan shell baru

display = new Display();

shell = new Shell(display);

shell.setSize(200, 200);

shell.setText("Hello SWT");

// Inisialisasi label

label1 = new Label(shell, SWT.LEFT);

label1.setText("Selamat Datang!");

Page 446: Java With Eclipse

label1.setBounds(10, 10, 150, 25); // set lokasi & ukuran kontrol

agar bisa ditampilkan

}

// Perintah "standar" SWT, harus ada pada setiap aplikasi SWT

public void run() {

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

/**

* @param args

*/

public static void main(String[] args) {

// Instansiasi kelas ini, kemudian jalankan run

HelloSWT2 hello = new HelloSWT2();

hello.run();

}

}

Berikut ini adalah hasilnya.

Ukuran yang Dibutuhkan

Setiap widget yang kita buat memiliki ukuran tertentu yang bisa dihitung secara otomatis,

sehingga programmer tidak perlu menerka atau mencoba-coba ukuran widget yang

diperlukan. Pada contoh di atas, kita harus menentukan sendiri ukuran suatu widget dengan

menggunakan setBounds(), akan tetapi kadang-kadang cara ini sulit dilakukan jika

jumlah yang kita gunakan beraneka ragam.

Page 447: Java With Eclipse

Kelas Control memiliki beberapa metode yang berhubungan dengan ukuran widget yang

dibutuhkan.

Metode Penjelasan

pack()Mengeset ukuran suatu kontrol secara otomatis, berdasarkan

ukuran minimum yang dibutuhkan oleh kontrol tersebut

pack(boolean berubah)

Mengeset ukuran suatu kontrol secara otomatis. Jika

berubah diisi true, maka ukuran kontrol akan dihitung

kembali, bukan diambil dari cache.

computeSize(int wHint,

int hHint)

Menghitung ukuran kontrol yang dibutuhkan. Sama dengan

computeSize(wHint, hHint, true). Mengembalikan

[code]Point

computeSize(int wHint,

int hHint,

boolean

berubah)

Menghitung ukuran kontrol yang dibutuhkan, dimana wHint

adalah lebar kira-kira, dan hHint adalah tinggi kira-kira. Jika

berubah diisi true maka ukuran kontrol akan dihitung

kembali, bukan diambil dari cache.

Metode pack() digunakan untuk mengeset suatu kontrol secar otomatis berdasarkan

ukuran minimum yang dibutuhkan oleh kontrol tersebut. Pada contoh label di atas, kita

bisa mengganti instruksi setBounds() dengan rangkaian perintah pack() dan

setLocation() untuk mengeset ukuran dan lokasinya secara terpisah. Misalnya,

...

// Inisialisasi label

label1 = new Label(shell, SWT.LEFT);

label1.setText("Selamat Datang!");

label1.pack(); // set ukuran kontrol otomatis

label1.setLocation(10, 10); // set lokasi kontrol

...

Metode computeSize() digunakan untuk menghitung ukuran yang dibutuhkan oleh suatu

kontrol sehingga kontrol tersebut bisa digambar dengan utuh. Dengan kata lain kita ingin

menjawab pertanyaan ini : berapa tinggi yang dibutuhkan jika lebarnya adalah x, atau

berapa lebar yang dibutuhkan jika tingginya adalah y, atau berapa lebar dan tinggi yang

dibutuhkan secara umum.

Untuk menjawab pertanyaan tersebut, kita membutuhkan dua variabel, yaitu salah satu

nilai "saran", yaitu nilai yang kita sudah ketahui, dan nilai SWT.DEFAULT untuk menandai

variabel yang ingin kita cari. Misalnya

Page 448: Java With Eclipse

Untuk menghitung tinggi widget jika lebarnya kira-kira 10 piksel, kita bisa

menggunakan label1.computeSize(10, SWT.DEFAULT)

Untuk menghitung lebar widget jika tingginya kira-kira 10 piksel, kita bisa

menggunakan label1.computeSize(SWT.DEFAULT, 10)

Untuk menghitung lebar dan tinggi widget yang dibutuhkan secara bersamaan, kita

bisa menggunakan label1.computeSize(SWT.DEFAULT,SWT.DEFAULT)

Event Ketika Kontrol Berpindah Tempat

Ketika suatu kontrol berpindah tempat, misalnya karena digeser atau ukurannya diubah

oleh user, event tertentu akan dilepaskan. Kita bisa memasang listener pada kontrol

tersebut untuk menangani event yang terjadi. Berikut ini adalah event yang terkait dengan

perubahan lokasi dan ukuran suatu kontrol.

Kelas Event

(event

bertipe)

Interface/Kelas

Listener

(listener

bertipe)

Metode (listener bertipe)

Jenis event

(event tanpa

tipe)

Penjelasan

controlMoved(ControlEvent) SWT.Move

Posisi suatu

kontrol

berubahControlEvent

ControlListener

(dan

ControlAdapter)controlResized(ControlEvent) SWT.Resize

Ukuran

kontrol

berubah

Contoh berikut membuat dua buah shell, yaitu shell utama dan shell kedua. Shell kedua

adalah shell kosong tanpa tepi mirip seperti tool tip. Jika shell utama berubah posisi atau

ukuran, shell kedua akan tergeser secara otomatis sehingga jaraknya tetap sama.

Berikut ini adalah kode sumbernya yang bisa diunduh di sini.

Page 449: Java With Eclipse

package com.lyracc.shellpindah;

import org.eclipse.swt.*;

import org.eclipse.swt.events.*;

import org.eclipse.swt.graphics.*;

import org.eclipse.swt.widgets.*;

public class ShellPindah {

// Variabel-variabel widget kita deklarasikan sebagai

// variabel privat

private Display display;

private Shell shell;

private Shell shellKedua;

// Jarak antara kedua shell

private int jarakShell = 20;

// Konstruktor kelas ini, untuk menginisialisasi semua widget

ShellPindah() {

// Membuat display dan shell baru

display = new Display();

shell = new Shell(display);

shell.setSize(200, 200);

shell.setText("Shell Pindah");

// Inisialisasi Shell kedua

shellKedua = new Shell(shell,SWT.TOOL);

shellKedua.setSize(100,100);

// Tambah listener jika shell utama berubah ukuran dan lokasi

shell.addControlListener(new ControlListener() {

@Override

public void controlMoved(ControlEvent e) {

pindah();

}

@Override

public void controlResized(ControlEvent e) {

pindah();

Page 450: Java With Eclipse

}

});

// Jangan lupa shell harus di-open..

shellKedua.open();

}

// Pindahkan shell kedua

private void pindah() {

// Ambil ukuran dan lokasi shell pertama

Rectangle pp = shell.getBounds();

// Set lokasi shell kedua

shellKedua.setLocation(

pp.x + pp.width + jarakShell,

pp.y);

}

// Perintah "standar" SWT, harus ada pada setiap aplikasi SWT

public void run() {

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

/**

* @param args

*/

public static void main(String[] args) {

// Instansiasi kelas ini, kemudian jalankan run

ShellPindah shellPindah = new ShellPindah();

shellPindah.run();

}

}

Mengeset Teks pada Label

Page 451: Java With Eclipse

Suatu label bisa diisi teks atau gambar, tapi tidak keduanya secara bersamaan. Berikut ini

adalah beberapa metode yang digunakan untuk mengeset dan mengambil teks dan gambar

pada label.

Metode Penjelasan

setText(String teks) Mengeset teks pada label

getText() Mengembalikan String yang merupakan teks pada label

Berikut ini adalah contoh mengeset teks dan gambar pada suatu label

Label label1 = new Label(shell, SWT.LEFT);

label1.setText("Selamat Datang!");

SWT.BORDER

SWT.BORDER adalah bit gaya yang tersedia pada Control. Semua kontrol, termasuk Label

bisa menggunakan bit gaya ini. Bit gaya ini memberikan garis tepi di sekitar kontrol.

Misalnya, dari contoh pada bagian sebelumnya, kita tambahkan SWT.BORDER sebagai bit

gaya pada label1, maka keluarannya ada seperti gambar berikut ini.

...

// Inisialisasi label

label1 = new Label(shell, SWT.LEFT | SWT.BORDER);

label1.setText("Selamat Datang!");

label1.setBounds(10, 10, 150, 25); // set lokasi & ukuran kontrol

agar bisa ditampilkan

...

Kerapatan Teks

Kita juga bisa mengatur kerapatan (alignment) suatu teks atau gambar. Selain

menggunakan bit gaya SWT.LEFT, SWT.RIGHT, atau SWT.CENTER pada konstruktor. Atau

kita juga bisa menggunakan metode instansi pada kelas tombol berikut :

Metode Penjelasan

setAlignment(int

rapatan)

Mengeset kerapatan suatu teks atau gambar, rapatan hanya

bisa berisi SWT.LEFT, SWT.RIGHT, atau SWT.CENTER

getAlignment()Mengambil int yang merupakan rapatan suatu teks atau gambar

pada label.

Misalnya,

Label labelTeks = new Label(shell, SWT.NONE); // Tanpa bit gaya

Page 452: Java With Eclipse

labelTeks.setText("Tombolku");

labelTeks.setAlignment(SWT.LEFT);

Ilustrasi berikut menunjukkan label dengan tiga kerapatan berbeda, menggunakan bit gaya

SWT.LEFT, SWT.RIGHT, dan SWT.CENTER.

Memangkas Teks

Jika kita ingin agar label yang kita tampilkan hanya terbatas pada ukuran tertentu saja, bit

gaya SWT.WRAP bisa digunakan untuk ini. SWT.WRAP memberitahukan kontrol untuk

memotong teks sehingga tidak ada teks yang terpotong dengan paksa. Berikut ini adalah

contoh perbedaan label yang menggunakan SWT.WRAP (label atas) dan yang tidak (label

bawah).

Kode lengkapnya adalah sebagai berikut :

import org.eclipse.swt.*;

import org.eclipse.swt.graphics.*;

import org.eclipse.swt.widgets.*;

public class HelloSWT3 {

// Variabel-variabel widget kita deklarasikan sebagai

// variabel privat

private Display display;

private Shell shell;

private Label label1;

private Label label2;

// Konstruktor kelas ini, untuk menginisialisasi semua widget

HelloSWT3() {

// Membuat display dan shell baru

display = new Display();

Page 453: Java With Eclipse

shell = new Shell(display);

shell.setSize(200, 200);

shell.setText("Hello SWT");

// Inisialisasi label

label1 = new Label(shell, SWT.LEFT | SWT.BORDER | SWT.WRAP);

label1.setText("Selamat Datang di sini");

label1.setLocation(10, 10); // set lokasi kontrol

// Hitung ukuran kontrol yang dibutuhkan

Point ukuran = label1.computeSize(SWT.DEFAULT, SWT.DEFAULT);

// Set lebar kontrol menjadi setengahnya

ukuran.x = ukuran.x/2;

label1.setSize(ukuran);

// Inisialisasi label

label2 = new Label(shell, SWT.LEFT | SWT.BORDER);

label2.setText("Selamat Datang di sini");

label2.setLocation(10, 40); // set lokasi kontrol

// Hitung ukuran kontrol yang dibutuhkan

ukuran = label2.computeSize(SWT.DEFAULT, SWT.DEFAULT);

// Set lebar kontrol menjadi setengahnya

ukuran.x = ukuran.x/2;

label2.setSize(ukuran);

}

// Perintah "standar" SWT, harus ada pada setiap aplikasi SWT

public void run() {

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

/**

* @param args

*/

Page 454: Java With Eclipse

public static void main(String[] args) {

// Instansiasi kelas ini, kemudian jalankan run

HelloSWT3 hello = new HelloSWT3();

hello.run();

}

}

Menggunakan Label sebagai Garis Pemisah

Kelas Label bisa digunakan untuk membuat garis pemisah, baik horizontal maupun

vertikal, yaitu dengan menggunakan bit gaya SWT.SEPARATOR yang digabungkan dengan

salah satu dari SWT.HORIZONTAL atau SWT.VERTICAL.

Garis pemisah biasanya digunakan untuk memisahkan item pada menu, atau memisahkan

pertanyaan dan tombol pada kotak dialog. Garis pemisah juga memiliki bit gaya lain, yaitu

SWT.SHADOW_IN, SWT.SHADOW_OUT dan SWT.SHADOW_NONE untuk menggambar beberapa

jenis garis pemisah, akan tetapi bit gaya ini tidak tersedia pada semua platform. Untuk

platform yang tidak tersedia seperti Linux, bit gaya ini tidak berpengaruh apa-apa.

Contoh kode berikut menggambar kedua jenis garis pemisah.

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

public class HelloSWT4 {

// Variabel-variabel widget kita deklarasikan sebagai

// variabel privat

private Display display;

private Shell shell;

private Label label1;

private Label label2;

private Label label3;

private Label label4;

Page 455: Java With Eclipse

// Konstruktor kelas ini, untuk menginisialisasi semua widget

HelloSWT4() {

// Membuat display dan shell baru

display = new Display();

shell = new Shell(display);

shell.setSize(250, 200);

shell.setText("Hello SWT");

// Inisialisasi label

label1 = new Label(shell, SWT.CENTER);

label1.setText("Vertikal");

label1.setBounds(10, 10, 100, 15);

label2 = new Label(shell, SWT.CENTER);

label2.setText("Horizontal");

label2.setBounds(120, 10, 100, 15);

label3 = new Label(shell, SWT.SEPARATOR | SWT.VERTICAL);

label3.setBounds(10, 40, 100, 100);

label4 = new Label(shell, SWT.SEPARATOR | SWT.HORIZONTAL);

label4.setBounds(120, 40, 100, 100);

}

// Perintah "standar" SWT, harus ada pada setiap aplikasi SWT

public void run() {

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

/**

* @param args

*/

public static void main(String[] args) {

// Instansiasi kelas ini, kemudian jalankan run

HelloSWT4 hello = new HelloSWT4();

hello.run();

}

Page 456: Java With Eclipse

}

Mengeset Warna

Kelas Control memiliki metode untuk mengeset latar belakang dan latar depan suatu

kontrol. Pada Label, latar belakang Label adalah ruangan di sekitar garis tepi atau wilayah

yang ditentukan dengan metode setBounds(), sedangkan latar depan Label adalah warna

teksnya.

Karena kelas Control adalah kelas super dari kontrol-kontrol lain, bukan hanya Label,

tetapi juga Button, Text dan Shell misalnya, maka metode-metode ini juga bisa

digunakan oleh kontrol-kontrol lain yang merupakan kelas turunan dari Control. Latar

belakang dan latar depan tiap-tiap kontrol berbeda-beda tergantung dari kontrolnya.

Berikut ini adalah beberapa metode pada kelas Control untuk mengatur warna.

Metode Penjelasan

setForeground(Color

warna)Mengeset warna latar depan suatu kontrol

setBackground(Color

warna)Mengeset warna latar belakang suatu kontrol

getForeground()Mengambil warna latar depan suatu kontrol. Tipe keluarannya

bertipe Color

getBackground()Mengambil warna latar belakang suatu kontrol. Tipe

keluarannya bertipe Color

Perhatikan bahwa metode-metode di atas bekerja dengan menggunakan input dan output

warna bertipe Color. Kelas Color terdapat pada paket org.eclipse.swt.graphics

Kelas Color

Instansi kelas Color digunakan untuk melambangkan warna yang sebenarnya yang telah

diberikan oleh sistem operasi.

Kelas ini bisa dibuat dengan dua cara : mengambil instansi warna dari sistem, dan

membuat objek sendiri dari paduan warna merah, hijau dan biru (RGB). Khusus untuk cara

yang kedua, programmer bertanggung jawab untuk menghapus objek ini dari memori

sistem dengan menggunakan dispose() setelah kita selesai menggunakannya.

Mengambil warna sistem

Page 457: Java With Eclipse

Cara termudah untuk mengisi kelas Color untuk digunakan dalam mengeset latar belakang

dan latar depan suatu kontrol adalah dengan mengambil warna sistem. Warna sistem terdiri

dari dua jenis, yaitu warna deskriptif dan warna sistem.

Warna deskriptif adalah warna seperti yang kita kenal, seperti merah, hijau, biru. Akan

tetapi setiap platform memiliki daftar warna tersendiri. Oleh karena itu, SWT menyimpan

warna-warna ini dalam konstanta pada kelas SWT, misalnya SWT.COLOR_RED. Warna

ini kita ubah menjadi objek bertipe Color dengan menggunakan perintah

getSystemColor(int konstantaWarna) yang terdapat pada objek berkelas Display,

misalnya

Color warna = display.getSystemColor(SWT.COLOR_RED);

Warna-warna deskriptif yang disimpan pada kelas SWT adalah sebagai berikut :

Warna

SWT.COLOR_WHITE

SWT.COLOR_BLACK

SWT.COLOR_RED

SWT.COLOR_DARK_RED

SWT.COLOR_GREEN

SWT.COLOR_DARK_GREEN

SWT.COLOR_YELLOW

SWT.COLOR_DARK_YELLOW

SWT.COLOR_BLUE

SWT.COLOR_DARK_BLUE

SWT.COLOR_MAGENTA

SWT.COLOR_DARK_MAGENTA

SWT.COLOR_CYAN

SWT.COLOR_DARK_CYAN

SWT.COLOR_GRAY

SWT.COLOR_DARK_GRAY

Warna sistem adalah warna yang dimiliki oleh sistem sebagai warna default suatu kontrol.

Misalnya, warna default latar belakang tombol selalu sama pada Windows, demikian juga

Page 458: Java With Eclipse

pada Linux. Jika warna ini diubah oleh user melalui Control Panel Windows, misalnya,

maka seluruh aplikasi akan mengikuti warna ini. Keuntungannya, warna aplikasi yang kita

buat akan selalu konsisten dengan warna sistem operasi. Warna sistem ini juga disimpan

sebagai konstanta pada kelas SWT, dan digunakan dengan cara yang sama dengan warna

deskriptif di atas.

Perlu diingat bahwa tidak semua sistem operasi memiliki kesemua warna ini. Untuk

mengetahui warna aktual suatu widget kita bisa menggunakan perintah getBackground()

dan getForeground seperti disebutkan di awal bagian ini.

Warna sistem yang disediakan oleh SWT adalah sebagai berikut

Warna Penjelasan

SWT.COLOR_WIDGET_DARK_SHADOWWarna bayangan

gelap suatu widget

SWT.COLOR_WIDGET_NORMAL_SHADOWWarna bayangan

normal suatu widget

SWT.COLOR_WIDGET_LIGHT_SHADOWWarna bayangan

terang suatu widget

SWT.COLOR_WIDGET_HIGHLIGHT_SHADOW

Warna bayangan

highlight suatu

widget

SWT.COLOR_WIDGET_FOREGROUNDWarna latar depan

widget

SWT.COLOR_WIDGET_BACKGROUNDWarna latar belakang

widget

SWT.COLOR_WIDGET_BORDER Warna tepi

SWT.COLOR_LIST_FOREGROUNDWarna latar depan

suatu list

SWT.COLOR_LIST_BACKGROUNDWarna latar belakang

suatu list

SWT.COLOR_LIST_SELECTIONWarna latar belakang

item yang dipilih

SWT.COLOR_LIST_SELECTION_TEXT Warna teks item yang

Page 459: Java With Eclipse

dipilih

SWT.COLOR_INFO_FOREGROUNDWarna latar depan

tool tip

SWT.COLOR_INFO_BACKGROUNDWarna latar belakang

tool tip

SWT.COLOR_TITLE_FOREGROUNDWarna latar depan

judul jendela

SWT.COLOR_TITLE_BACKGROUNDWarna latar belakang

judul jendela

SWT.COLOR_TITLE_BACKGROUND_GRADIENT

Warna gradasi latar

belakang judul

jendela (hanya pada

Windows lama,

seperti Windows

2000)

SWT.COLOR_TITLE_INACTIVE_FOREGROUND

Warna latar depan

judul jendela yang

tidak aktif

SWT.COLOR_TITLE_INACTIVE_BACKGROUND

Warna latar belakang

judul jendela yang

tidak aktif

SWT.COLOR_TITLE_INACTIVE_BACKGROUND_GRADIENT

Warna gradasi latar

belakang judul

jendela yang tidak

aktif (hanya pada

Windows lama,

seperti Windows

2000)

Kelas RGB

Kelas ini merupakan representasi dari warna merah, hijau dan biru (red, green, blue =

RGB), yang merupakan komposisi suatu warna di layar. Kelas RGB berbeda dengan kelas

Page 460: Java With Eclipse

Color di mana RGB hanya berisi representasi dari warna tersebut, bukan merupakan warna

yang sesungguhnya yang bisa diolah atau diberikan kepada suatu kontrol.

Instasiasi dari kelas RGB bisa dibuat dengan menggunakan konstruktor dengan bentuk

RGB(int merah, int hijau, int biru).

Karena tidak menggunakan memori dari sistem operasi, maka seringkali instansi kelas ini

digunakan dalam transaksi data dengan widget lain, misalnya pada ColorDialog, warna

yang dipilih oleh user akan disimpan dalam bentuk RGB.

Seperti disebutkan sebelumnya, instansi kelas ini tidak bisa digunakan langsung untuk

mengeset latar belakang atau latar depan suatu kontrol. Untuk bisa digunakan, kita harus

meminta alokasi dari sistem operasi, yaitu dalam bentuk objek bertipe Color. Caranya

yaitu dengan menggunakan salah satu konstruktor kelas Color yang berbentuk seperti

Color(Device perangkat, RGB rgb)

Di sini perangkat bisa berupa display atau printer. Setelah kita berhasil meminta warna

dari sistem operasi, kita bisa mengecek apakah warna yang dihasilkan sesuai dengan yang

kita berikan dengan menggunakan metode pada kelas Color, yaitu

getRed() untuk mendapatkan warna merah

getGreen untuk mendapatkan warna hijau

getBlue() untuk mendapatkan warna biru

getRGB() untuk mendapatkan objek bertipe RGB yang merupakan gabungan merah,

hijau dan biru.

Apakah ini berarti bahwa warna yang kita minta belum tentu sama dengan warna yang

diberikan suatu perangkat? Jawabannya benar. Untuk perangkat yang memiliki warna tak

terbatas, warna yang kita set akan sama dengan warna yang kita dapat. Ada beberapa

perangkat yang hanya memiliki sejumlah warna tertentu. Artinya, warna-warna yang

didukung oleh perangkat ini terbatas jumlahnya. Ketika sistem operasi meminta warna dari

perangkat ini, warna yang terdekat yang akan diberikan kembali kepada kita. Ini sangat

berguna untuk menjamin portabilitas SWT di berbagai platform dan perangkat.

Mari kita lihat contoh berikut, di mana label akan kita berikan warna latar depan dan warna

latar belakang yang berbeda dari defaultnya. Program lengkapnya bisa Anda unduh di sini.

Page 461: Java With Eclipse

package com.lyracc.warnalabel;

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

import org.eclipse.swt.graphics.*;

public class WarnaLabel {

// Variabel-variabel widget kita deklarasikan sebagai

// variabel privat

private Display display;

private Shell shell;

private Label label1;

private Label label2;

private Color warna1;

private Color warna2;

// Konstruktor kelas ini, untuk menginisialisasi semua widget

WarnaLabel() {

// Membuat display dan shell baru

display = new Display();

shell = new Shell(display);

shell.setSize(250, 200);

shell.setText("Hello SWT");

// Inisialisasi label

label1 = new Label(shell, SWT.CENTER | SWT.BORDER);

label1.setText("Hello dari SWT");

label1.setBounds(10, 10, 200, 25);

// Set warna, cara pertama dengan mengambil warna dari sistem

label1.setBackground(display.getSystemColor(SWT.COLOR_BLUE));

Page 462: Java With Eclipse

label1.setForeground(display.getSystemColor(SWT.COLOR_YELLOW));

label2 = new Label(shell, SWT.CENTER | SWT.BORDER);

label2.setText("Hallo dari SWT");

label2.setBounds(10, 40, 200, 25);

// Set warna, cara kedua dengan membuat objek Color dan RGB

warna1 = new Color(display, new RGB(255,255,0)); // menggunakan

objek RGB

warna2 = new Color(display, 0, 0, 255); // konstruktor Color

jenis lain

label2.setBackground(warna1);

label2.setForeground(warna2);

warna1.dispose(); // objek ini harus dihapus manual dari memori

warna2.dispose(); // objek ini harus dihapus manual dari memori

}

// Perintah "standar" SWT, harus ada pada setiap aplikasi SWT

public void run() {

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

/**

* @param args

*/

public static void main(String[] args) {

// Instansiasi kelas ini, kemudian jalankan run

WarnaLabel jendela = new WarnaLabel();

jendela.run();

}

}

Mengeset Huruf

Kelas Control memiliki metode untuk mengeset huruf kontrol. Berikut ini adalah

beberapa metode pada kelas Control untuk mengatur warna.

Metode Penjelasan

Page 463: Java With Eclipse

setFont(Font huruf) Mengeset huruf suatu kontrol

getFont() Mengambil huruf suatu kontrol. Tipe keluarannya bertipe Font

Perhatikan bahwa metode-metode di atas bekerja dengan menggunakan input dan output

huruf bertipe Font. Kelas Font terdapat pada paket org.eclipse.swt.graphics.

Seperti pada warna dengan Color dan RGB, huruf memiliki dua kelas sejenis, yaitu Font

dan FontData. Kelas Font, seperti Color adalah huruf yang sudah dialokasikan oleh

sistem operasi, sedangkan FontData seperti RGB adalah representasi huruf yang belum

dialokasi oleh sistem operasi.

Ingat bahwa jenis huruf sangat dipengaruhi oleh sistem operasi. Jenis huruf tertentu hanya

ada pada sistem operasi tertentu, sehingga sangat sulit untuk membuat aplikasi yang

bergantung pada huruf tertentu. Kita bisa juga mengontrol penggunaan huruf hingga detail,

misalnya jeda antar huruf, tinggi huruf dan lainnya, tapi dalam prakteknya, teknik ini

jarang digunakan untuk memodifikasi suatu kontrol. Kecuali jika Anda ingin membuat

widget sendiri, atau ingin menggambar langsung pada kanvas. Kita tidak akan membahas

hingga ke level detail seperti ini.

Kelas Font

Instansi kelas Font digunakan untuk melambangkan huruf yang sebenarnya yang telah

diberikan oleh sistem operasi.

Kelas ini bisa hanya bisa dibuat dengan membuat objek sendiri dengan menggunakan

konstruktornya. Programmer bertanggung jawab untuk menghapus objek ini dari memori

sistem dengan menggunakan dispose() setelah kita selesai menggunakannya. Berikut ini

adalah konstruktor kelas Font yang tersedia :

Konstruktor Penjelasan

Font(Device device,

FontData fd)

Membuat huruf pada suatu perangkat (baik display atau

printer) dengan input FontData

Font(Device device,

FontData[] fds)

Membuat huruf pada suatu perangkat (baik display atau

printer) dengan input beberapa FontData yang tersusun

dalam bentuk array (hanya Mac OS yang bisa memiliki lebih

dari satu jenis huruf)

Font(Device device,

String name, int

height, int style)

Membuat huruf pada suatu perangkat (baik display atau

printer) dengan input nama, tinggim dan gayanya. Gaya

adalah salah satu atau gabungan dari SWT.NORMAL, SWT.BOLD,

Page 464: Java With Eclipse

dan SWT.ITALIC

Mengambil instansi FontData dari Font

Misalnya kita hanya ingin mengubah ukuran huruf dari suatu kontrol. Cara yang paling

mudah dan bisa bekerja untuk semua platform adalah dengan mengambil huruf yang

digunakan suatu kontrol, dengan menggunakan getFont(), kemudian mengambil instansi

FontData dari Font yang dihasilkan. Kemudian kita konstruksi lagi Font baru dengan

menambahkan ukuran huruf, atau gayanya. Dengan huruf yang baru, kita gunakan

setFont() untuk mengaplikasikannya pada kontrol kita.

Untuk mengambil FontData dari suatu Font, kita bisa gunakan metode instansi

getFontData() dari kelas Font. Akan tetapi yang dikembalikan adalah array dari

FontData. Pada Windows hanya ada satu huruf yang dikembalikan, akan tetapi pada Mac

OS, beberapa huruf bisa dikembalikan. Misalnya lihat contoh berikut ini.

....

Font huruf = label1.getFont();

FontData[] dataHuruf = huruf.getFontData();

....

Kelas FontData

Kelas FontData bisa diambil dari suatu widget (seperti contoh sebelumnya), dari sistem

operasi (mengambil huruf yang disediakan oleh sistem operasi), atau membuatnya sendiri.

Untuk mengambil FontData yang tersedia pada suatu perangkat, kita bisa menggunakan

metode instansi getFontList(String namaFace, boolean scalable) dari suatu

perangkat yang dikembalikan dalam bentuk array. namaFace adalah nama huruf yang akan

kita ambil, dan jika scalable bernilai true, hanya huruf yang bisa diperbesar/diperkecil

tanpa mengurangi keindahan huruf saja yang akan diambil. Misalnya contoh berikut

mengambil semua huruf yang tersedia pada display,

FontData[] daftarHuruf = display.getFontList(null, true);

Untuk membuat FontData dari konstruktornya, tersedia beberapa jenis konstruktor, yaitu

Konstruktor Penjelasan

FontData() Membuat instansi FontData kosong

FontData(String string)

Membuat instansi FontData dengan input string yang

menjelaskan nama dan ukuran huruf, dengan format seperti

yang dikembalikan oleh metode toString()

FontData(String nama, Membuat instansi FontData dengan input nama, tinggi huruf

Page 465: Java With Eclipse

int tinggi, int gaya) dan gaya. Gaya huruf bisa merupakan salah satu atua

kombinasi dari SWT.NORMAL, SWT.BOLD dan SWT.ITALIC

Untuk memanipulasi FontData berikut ini adalah beberapa metode yang bisa digunakan

Metode Penjelasan

getName() Mengembalikan String yang merupakan nama huruf

setName(String nama) Mengeset nama huruf

getStyle() Mengembalikan int yang merupakan gaya huruf

setStyle(int gaya)Mengeset gaya huruf, salah satu atau kombinasi dari

SWT.NORMAL, SWT.BOLD dan SWT.ITALIC

getHeight() Mengembalikan int yang merupakan tinggi atau ukuran huruf

setHeight(int

tinggi)Mengeset tinggi huruf

getLocale() Mengambil kedaerahan (locale) huruf dalam bentuk String

setLocale(String

locale)Mengeset kedaerahan huruf

Mari kita simak contoh berikut ini. Kita akan membuat label dengan miring dan tebal dari

suatu kontrol. Ukurannya juga akan kita ubah menjadi 2 kali lipatnya. Algoritmanya bisa

dituangkan sebagai berikut :

Ambil Font dari label tersebut

Ambil FontData dari Font

Modifikasi setiap FontData yang dikembalikan

Buat Font baru

Gunakan setFont() untuk mengeset huruf ke label tersebut

Algoritma tersebut dapat kita tuangkan seperti

...

Font huruf = label1.getFont();

FontData[] dataHuruf = huruf.getFontData();

for (int i = 0; i < dataHuruf.length; i++) {

dataHuruf[i].setStyle(SWT.BOLD | SWT.ITALIC);

dataHuruf[i].setHeight(dataHuruf[i].getHeight() * 2);

}

Font hurufBaru = new Font(display, dataHuruf);

label1.setFont(hurufBaru);

Page 466: Java With Eclipse

hurufBaru.dispose();

...

Kita modifikasi sedikit program kita sebelumnya untuk menampilkan label dengan huruf

dan warna berbeda. Program lengkapnya bisa diunduh di sini.

package com.lyracc.hurufdanwarnalabel;

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

import org.eclipse.swt.graphics.*;

public class HurufDanWarnaLabel {

// Variabel-variabel widget kita deklarasikan sebagai

// variabel privat

private Display display;

private Shell shell;

private Label label1;

private Label label2;

private Color warna1;

private Color warna2;

// Konstruktor kelas ini, untuk menginisialisasi semua widget

HurufDanWarnaLabel() {

// Membuat display dan shell baru

display = new Display();

shell = new Shell(display);

shell.setSize(500, 200);

shell.setText("Hello SWT");

// Inisialisasi label

label1 = new Label(shell, SWT.CENTER | SWT.BORDER);

Page 467: Java With Eclipse

label1.setText("Hello dari SWT");

label1.setBounds(10, 10, 400, 50);

// Set warna, cara pertama dengan mengambil warna dari sistem

label1.setBackground(display.getSystemColor(SWT.COLOR_BLUE));

label1.setForeground(display.getSystemColor(SWT.COLOR_YELLOW));

// Modifikasi huruf

Font huruf = label1.getFont();

FontData[] dataHuruf = huruf.getFontData();

for (int i = 0; i < dataHuruf.length; i++) {

dataHuruf[i].setStyle(SWT.BOLD | SWT.ITALIC);

dataHuruf[i].setHeight(dataHuruf[i].getHeight() * 2);

}

Font hurufBaru = new Font(display, dataHuruf);

label1.setFont(hurufBaru);

hurufBaru.dispose();

label2 = new Label(shell, SWT.CENTER | SWT.BORDER);

label2.setText("Hallo dari SWT");

label2.setBounds(10, 70, 200, 25);

// Set warna, cara kedua dengan membuat objek Color dan RGB

warna1 = new Color(display, new RGB(255,255,0)); // menggunakan

objek RGB

warna2 = new Color(display, 0, 0, 255); // konstruktor Color

jenis lain

label2.setBackground(warna1);

label2.setForeground(warna2);

warna1.dispose(); // objek ini harus dihapus manual dari memori

warna2.dispose(); // objek ini harus dihapus manual dari memori

}

// Perintah "standar" SWT, harus ada pada setiap aplikasi SWT

public void run() {

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

Page 468: Java With Eclipse

}

/**

* @param args

*/

public static void main(String[] args) {

// Instansiasi kelas ini, kemudian jalankan run

HurufDanWarnaLabel jendela = new HurufDanWarnaLabel();

jendela.run();

}

}

Menampilkan gambar

Seperti halnya warna dan huruf pada SWT, ada dua kelas yang digunakan untuk

menampilkan gambar pada SWT : ImageData untuk melambangkan gambar dalam format

yang tidak tergantung pada suatu perangkat, dan Image yang merupakan representasi

gambar pada suatu perangkat.

Suatu kontrol memiliki beberapa metode untuk menampilkan gambar di latar depan

ataupun latar belakangnya. Tentunya, ini tergantung dari kontrol tersebut bagaimana hal ini

akan diimplementasikan. Pada Label misalnya, latar belakang bisa diset berbentuk gambar

menggantikan warna latar belakangnya. Atau bisa juga label diisi dengan gambar sebagai

pengganti teksnya.

Berikut ini adalah beberapa metode untuk mengeset suatu gambar pada kontrol.

Metode Penjelasan

setBackgroundImage(Image

gambar)

Mengeset latar belakang kontrol dengan gambar

sebagai pengganti warna latar belakang

setImage(Image gambar) Mengeset gambar sebagai pengganti teks pada kontrol

getBackgroundImage()Mengembalikan Image yang merupakan gambar latar

belakang suatu kontrol

getImage()Mengembalikan Image yang merupakan gambar pada

kontrol

Seperti warna dan huruf suatu kontrol yang memiliki pasangan Color dan RGB atau Font

dan FontData, gambar juga memiliki pasangan Image dan ImageData. ImageData bisa

juga dipergunakan untuk melakukan pengolahan gambar sederhana, misalnya mengganti

kontras melakukan clipping, dan sebagainya, akan tetapi kita tidak akan membahas terlalu

Page 469: Java With Eclipse

detail tentang gambar pada bagian ini. Kita akan bahas lebih lanjut bagaimana untuk

mengeset gambar latar depan dan latar belakang suatu kontrol.

Kelas Image

Kelas Image bisa diinstansiasi ke dalam bentuk objek dengan menggunakan beberapa

konstruktor. Salah satunya yang paling mudah digunakan adalah bentuk konstruktor seperti

Image(Device device, String namaFileGambar), di mana device adalah perangkat

yang akan digunakan untuk menampilkan gambar, bisa display atau printer, dan

namaFileGambar adalah path di mana file gambar yang akan kita tampilkan berada.

Misalnya, pada perintah berikut

Image gambar = new Image(display, "contoh.png");

digunakan untuk mengimport gambar ke dalam objek bernama gambar. Objek ini bisa kita

gunakan untuk mengeset gambar latar belakang atau pengganti teks pada suatu kontrol.

Akan tetapi ingat objek ini juga harus dihapus dari sistem operasi dengan memanggil

gambar.dispose() secara eksplisit setelah tidak lagi digunakan.

Jika ukuran suatu kontrol lebih besar dari pada gambar yang akan kita tampilkan di latar

belakangnya, maka gambar tersebut akan diulang-ulang untuk memenuhi tampilan suatu

kontrol. Kita bisa menggunakan metode instansi getBounds() dari suatu Image untuk

mendapatkan ukuran gambar yang diperlukan. Hasilnya bisa kita gunakan untuk mengeset

kontrol yang akan kita gunakan untuk menampilkan gambar.

Contoh berikut menampilkan dua jenis label, di mana yang pertama menampilkan gambar

sebagai warna latar belakangnya, dan yang satu menampilkan gambar sebagai pengganti

teks. Program lengkapnya dapat diunduh di sini.

package com.lyracc.gambarlabel;

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

import org.eclipse.swt.graphics.*;

Page 470: Java With Eclipse

public class GambarLabel {

// Variabel-variabel widget kita deklarasikan sebagai

// variabel privat

private Display display;

private Shell shell;

private Label label1;

private Label label2;

// Konstruktor kelas ini, untuk menginisialisasi semua widget

GambarLabel() {

// Membuat display dan shell baru

display = new Display();

shell = new Shell(display);

shell.setSize(250, 200);

shell.setText("Hello SWT");

// Inisialisasi label

label1 = new Label(shell, SWT.CENTER | SWT.BORDER);

label1.setText("Hello dari SWT");

label1.setBounds(10, 10, 200, 50);

label1.setForeground(display.getSystemColor(SWT.COLOR_RED));

// Inisialisasi gambar

Image gambar = new Image(display,"contoh.png");

label1.setBackgroundImage(gambar); // Set gambar sebagai latar

belakang

label2 = new Label(shell, SWT.CENTER | SWT.BORDER);

label2.setBounds(10, 70, 200, 50);

label2.setImage(gambar); // Set gambar

gambar.dispose(); // Jangan lupa menghapus gambar dari memori

}

// Perintah "standar" SWT, harus ada pada setiap aplikasi SWT

public void run() {

shell.open();

Page 471: Java With Eclipse

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

/**

* @param args

*/

public static void main(String[] args) {

// Instansiasi kelas ini, kemudian jalankan run

GambarLabel jendela = new GambarLabel();

jendela.run();

}

}

Button (Tombol)

Kelas Button melambangkan suatu tombol yang jika ditekan atau dilepaskan akan

melepaskan event tertentu.

Kelas Button memiliki beberapa bit gaya :

Bit gayaTampilan (pada

Linux)Keterangan

SWT.ARROW

SWT.CHECK

SWT.PUSH

SWT.RADIO

SWT.TOGGLE

Hanya salah satu dari gaya ini yang boleh digunakan

SWT.FLAT

Menampilkan tombol dengan tampilan "rata", bukan

seperti tombol biasa yang memiliki tampilan 3D seperti

tombol tekan. (Hanya untuk beberapa platform tertentu

saja)

SWT.LEFT Hanya salah satu dari gaya ini yang boleh digunakan

Page 472: Java With Eclipse

SWT.RIGHT

SWT.CENTER

SWT.UP

SWT.DOWN

SWT.LEFT

SWT.RIGHT

Hanya boleh digunakan bersama dengan SWT.ARROW

Suatu tombol bisa diisi teks atau gambar, tapi tidak keduanya secara bersamaan. Teks yang

diisi pada suatu tombol tidak dapat berisi baris baru '\n' atau '\r'. Berikut ini adalah

beberapa metode yang digunakan untuk mengeset dan mengambil teks dan gambar pada

tombol.

Metode Penjelasan

setText(String teks) Mengeset teks pada tombol

getText() Mengembalikan String yang merupakan teks pada tombol

setImage(Image image) Mengeset gambar pada tombol

getImage() Mengembalikan Image yang merupakan gambar pada tombol

Berikut ini adalah contoh mengeset teks dan gambar pada suatu tombol :

Button tombolTeks = new Button(shell, SWT.PUSH);

tombolTeks.setText("Tombolku");

ImageData dataGambar = new ImageData("c:\\grafik.bmp");

Image gambar = new Image(display, dataGambar);

Button tombolGambar = new Button(shell, SWT.PUSH);

tombolGambar.setImage(gambar);

Kita juga bisa mengatur kerapatan (alignment) suatu teks atau gambar. Selain

menggunakan bit gaya SWT.LEFT, SWT.RIGHT, atau SWT.CENTER pada konstruktor. Atau

kita juga bisa menggunakan metode instansi pada kelas tombol berikut :

Metode Penjelasan

setAlignment(int

rapatan)

Mengeset kerapatan suatu teks atau gambar, rapatan hanya bisa

berisi SWT.LEFT, SWT.RIGHT, atau SWT.CENTER

Page 473: Java With Eclipse

getAlignment()Mengambil int yang merupakan rapatan suatu teks atau gambar

pada tombol.

Misalnya,

Button tombolTeks = new Button(shell, SWT.PUSH);

tombolTeks.setText("Tombolku");

tombolTeks.setAlignment(SWT.LEFT);

Tombol SWT.PUSH

Tombol ini memiliki sifat tombol biasa, yaitu ketika di klik tombol akan tampil seperti

ditekan, dan ketika dilepas maka tombol akan kembali seperti biasa. Tombol ini memiliki

event berikut :

Kelas

Event

(event

bertipe)

Interface/Kel

as Listener

(listener

bertipe)

Metode (listener bertipe)

Jenis event

(event tanpa

tipe)

Penjelas

an

widgetDefaultSelected(Selectio

nEvent)

SWT.DefaultSele

ction

Suatu

aksi

pemiliha

n

dilakuka

n pada

pilihan

default

(misalny

a ketika

tombol

Enter

ditekan)

SelectionEv

ent

SelectionListe

ner (dan

SelectionAdap

ter)

widgetSelected(SelectionEvent) SWT.Selection

Suatu

aksi

pemiliha

n

dilakuka

n dalam

Page 474: Java With Eclipse

widget

(misalny

a

memilih

item

pada

drop

down list

Misalnya untuk menambahkan listener untuk event yang terjadi ketika tombol ditekan, kita

bisa menggunakan SWT.Selection, misalnya pada contoh berikut :

Button tombolTeks = new Button(shell, SWT.PUSH);

tombolTeks.setText("Tombolku");

tombolTeks.addSelectionListener(new SelectionAdapter() {

public void widgetSelected(SelectionEvent e) {

// Perintah-perintah lainnya

}

});

Tombol SWT.ARROW

Tombol ini secara perilaku mirip dengan SWT.PUSH, akan tetapi perbedaannya tombol ini

tidak bisa diisi teks atau gambar. Tombol ini akan menggambar tanda panah, yang arahnya

tergantung dari bit gaya yang diberikan, bisa ke kiri, kanan, atas atau bawah.

Tombol SWT.TOGGLE

Berbeda dengan tombol SWT.PUSH, tombol ini ketika diklik akan berada dalam posisi

tertekan terus walaupun mouse sudah dilepaskan. Untuk mengembalikannya, tombol ini

harus diklik kembali. Karena tombol ini bisa memiliki dua keadaan, yaitu keadaan tertekan

dan keadaan terlepas, ada dua metode yang kita bisa gunakan untuk mengeset keadaan

tombol atau untuk mengambil keadaan tombol saat itu, yaitu

Metode Penjelasan

setSelection(boole

an pilih)

Mengeset keadaan suatu tombol. Jika pilih bernilai true, maka

tombol berada dalam keadaan

tertekan.</td></tr><tr><td>getSelection()</td><td>Mengemb

alikan [code]boolean yang berisi keadaan suatu tombol apakah

dalam keadaan tertekan atau terlepas

Tombol SWT.CHECK

Page 475: Java With Eclipse

Tombol SWT.CHECK tidak berbentuk seperti tombol, akan tetapi perilakunya mirip

dengan SWT.TOGGLE. Secara tampilan, SWT.CHECK ditampilkan seperti tombol kotak

tik, yang apabila diklik kotak tik akan tercontreng. Untuk menghilangkan contrengan, klik

kembali kotak cek tersebut.

Karenanya, mirip seperti SWT.TOGGLE, tombol ini memiliki dua keadaan tetap, yaitu

kondisi tercontreng dan tidak tercontreng. Untuk mengeset keadaan ini, gunakan metode

yang sama dengan SWT.TOGGLE, yaitu setSelection() dan getSelection().

Tombol SWT.RADIO

Tombol ini mirip seperti SWT.CHECK, di mana apabila kita mengklik tombol radio, maka

tombol tersebut berada dalam keadaan terpilih. Perbedaannya, jika beberapa tombol

SWT.RADIO berada dalam satu induk, maka hanya satu tombol radio saja yang bisa aktif.

Artinya jika kita mengklik tombol radio kedua, maka tombol ini akan terpilih dan yang

lainnya akan mati. Untuk mengesetnya, gunakan metode yang sama seperti pada

SWT.CHECK dan SWT.TOGGLE.

Biasanya, hanya satu tombol radio pada suatu kotak dialog atau jendela tidak masuk akal.

Karena hanya ada tombol radio yang bisa aktif, maka satu tombol radio ini akan selalu

aktif, sehingga percuma untuk diletakkan dalam GUI. Jika ada dua tombol radio,

perilakunya persis sama dengan satu tombol SWT.CHECK (jika satu aktif, maka yang

lainnya pasti tidak aktif). Untuk itu tombol radio biasanya digunakan untuk meminta user

memilih satu dari sekian banyak pilihan.

Berikut ini adalah contoh program yang bisa Anda unduh di sini. Program ini akan

menggambar beberapa jenis tombol. Jika salah satu tombol ditekan, maka warna latar

belakang jendela akan berubah-ubah. Warna-warna ini disimpan dalam palette warna

dalam bentuk array. Jika warna terakhir sudah ditampilkan, kemudian tombol ditekan

kembali, maka warna pertama akan ditampilkan kembali.

package com.lyracc.tombolwarna;

Page 476: Java With Eclipse

import org.eclipse.swt.*;

import org.eclipse.swt.graphics.*;

import org.eclipse.swt.layout.*;

import org.eclipse.swt.widgets.*;

import org.eclipse.swt.events.*;

public class TombolWarna {

// Variabel-variabel widget kita deklarasikan sebagai

// variabel privat

private Display display;

private Shell shell;

private Color[] warna;

private int warnaSekarang = 0;

final int JUMLAHWARNA = 10;

// Konstruktor kelas ini, untuk menginisialisasi semua widget

TombolWarna() {

// Membuat display dan shell baru

display = new Display();

shell = new Shell(display);

shell.setLayout(new RowLayout());

// Ubah ukuran jendela menjadi 200 x 200 piksel

shell.setSize(250, 200);

// Inisialisasi tombol-tombol

Button tombol = new Button(shell,SWT.PUSH);

tombol.setText("Tombolku SWT.PUSH");

Button tombol1 = new Button(shell,SWT.ARROW | SWT.LEFT);

Button tombol2 = new Button(shell,SWT.TOGGLE);

tombol2.setText("Tombolku SWT.TOGGLE");

Button tombol3 = new Button(shell,SWT.CHECK);

tombol3.setText("Tombolku SWT.CHECK");

Button tombol4 = new Button(shell,SWT.RADIO);

tombol4.setText("Tombolku SWT.RADIO");

Page 477: Java With Eclipse

// Mengeset event

ListenerTombolWarna listenerTombolWarna = new

ListenerTombolWarna();

tombol.addSelectionListener(listenerTombolWarna);

tombol1.addSelectionListener(listenerTombolWarna);

tombol2.addSelectionListener(listenerTombolWarna);

tombol3.addSelectionListener(listenerTombolWarna);

tombol4.addSelectionListener(listenerTombolWarna);

// Mengeset warna-warna

warna = new Color[JUMLAHWARNA];

warna[0] = display.getSystemColor(SWT.COLOR_BLACK);

warna[1] = display.getSystemColor(SWT.COLOR_BLUE);

warna[2] = display.getSystemColor(SWT.COLOR_CYAN);

warna[3] = display.getSystemColor(SWT.COLOR_GRAY);

warna[4] = display.getSystemColor(SWT.COLOR_GREEN);

warna[5] = display.getSystemColor(SWT.COLOR_MAGENTA);

warna[6] = display.getSystemColor(SWT.COLOR_RED);

warna[7] = display.getSystemColor(SWT.COLOR_WHITE);

warna[8] = display.getSystemColor(SWT.COLOR_YELLOW);

warna[9] =

display.getSystemColor(SWT.COLOR_WIDGET_BACKGROUND);

}

// Kelas listener ketika tombol ditekan

class ListenerTombolWarna extends SelectionAdapter {

public void widgetSelected(SelectionEvent e) {

// Jika warna lebih sudah di akhir array

// kembali ke warna 0

if (warnaSekarang == warna.length)

warnaSekarang = 0;

shell.setBackground(warna[warnaSekarang++]);

}

}

// Perintah "standar" SWT, harus ada pada setiap aplikasi SWT

public void run() {

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

Page 478: Java With Eclipse

display.sleep();

display.dispose();

}

/**

* @param args

*/

public static void main(String[] args) {

// Instansiasi kelas ini, kemudian jalankan run

TombolWarna tombolWarna = new TombolWarna();

tombolWarna.run();

}

}

Text

Kelas Text digunakan sehingga user bisa mengetikkan atau mengedit suatu teks. Kelas ini

juga merupakan turunan dari kelas Control sehingga memiliki metode, bit gaya, dan event

seperti pada kelas Control lainnya. Berikut ini adalah metode, bit gaya, dan event khusus

untuk kelas Text

Bit GayaTampilan (pada

Linux)Keterangan Aturan

SWT.BORDERMembuat garis tepi di

sekitar teks

SWT.SINGLEMembuat input teks satu

baris

SWT.MULTIMembuat input teks

banyak baris

Hanya boleh salah satu

SWT.SINGLE atau

SWT.MULTI

SWT.LEFT Kerapatan teks kiri

SWT.CENTER Kerapatan teks tengah

SWT.RIGHT Kerapatan teks kanan

Hanya boleh salah satu

SWT.PASSWORD Input teks password

SWT.READ_ONLY Membuat input teks

Page 479: Java With Eclipse

hanya bisa dibaca

SWT.WRAP

Membuat input teks yang

terpotong (bukan

tergulung).

Variabel Keterangan

DELIMITERKarakter pemisah baris. Biasanya mengikuti sistem operasi (Windows "\r\n"

dan Linux "\n") akan tetapi kita bisa atur sesuai kebutuhan

LIMIT Maksimum karakter yang bisa dimasukkan ke dalam input teks

Kontrol Text adalah elemen user interface yang digunakan sehingga user bisa

memasukkan atau mengedit string. Kontrol ini bisa dipilih. Pilhannya merupakan tempat di

mana karakter akan dimasukkan. Jika pilihannya lebih dari satu karakter, kontrol Text

akan menggambar karakter yang dipilih berbeda dengan karakter yang tidak dipilih

(misalnya diberi warna latar belakang biru atau hitam, sedangkan teks yang dipilih

berwarna terang). Jika pilihannya tidak ada, maka akan digambar seperti caret yang

bentuknya seperti huruf I. Caret adalah garis vertikal yang muncul di antara karakter. Caret

ini memberi petunjuk kepada user dimana karakter yang diketik akan dimasukkan.

Gambar berikut adalah caret yang diletakkan di akhir teks. Di bawahnya, ketika salah satu

karakter dipilih.

Kontrol Text hanya mendukung teks "biasa". Artinya semua karakter pada kontrol ini

memiliki jenis huruf dan warna yang sama. Jika kita membutuhkan kontrol untuk mengedit

teks yang lebih fleksibel, kita bisa menggunakan org.eclipse.swt.custom.StyledText

yang didesain untuk Eclipse. Perhatikan bahwa StyledText bukan widget alami bawaan

sistem operasi.

Suatu program aplikasi bisa mendeteksi bahwa sesuatu dalam kontrol teks telah berubah,

atau untuk memfilter huruf/angka ketika diketik oleh user. Misalnya, suatu aplikasi

memberi teks peringatan bahwa hanya angka dari 0-9 saja yang bisa dimasukkan. Untuk

itu, aplikasi akan mendengarkan event SWT.Modify dan SWT.Verify yang akan dijelaskan

kemudian.

Ada dua jenis kontrol teks : yang memiliki satu baris teks dan yang memiliki beberapa

baris teks.

Page 480: Java With Eclipse

Kontrol Text Satu Baris dan Multi Baris

Bit gaya SWT.SINGLE digunakan untuk membuat kontrol teks satu baris. Potongan kode

berikut membuat kontrol teks satu baris yang dilengkapi dengan garis tepi, kemudian

mengisi teksnya dengan "asd".

Text teks1 = new Text(shell, SWT.BORDER | SWT.SINGLE);

teks1.setText("asd");

Kontrol teks banyak baris dibuat dengan menggunakan bit gaya SWT.MULTI. Tidak seperti

kontrol teks satu baris, kontrol teks banyak baris bisa kita lengkapi dengan SWT.H_SCROLL

atau SWT.V_SCROLL pada bit gayanya untuk membuat scroll bar horizontal dan vertikal.

Berikut ini adalah potongan kontrol banyak baris dengan garis tepi dan scroll bar, dengan

teks yang cukup panjang.

teks2 = new Text(shell, SWT.BORDER | SWT.MULTI | SWT.H_SCROLL |

SWT.V_SCROLL);

teks2.setText("Hallo SWT, saya sedang belajar " + Text.DELIMITER +

"membuat program dengan SWT");

teks2.setSize(100, 70);

Operasi String

Kontrol Text memiliki beberapa metode untuk mengisi dan mengambil string dari widget

mirip seperti Label dan Button.

Metode Penjelasan

setText(String

s)

Mengeset isi teks. Isi sebelumnya dihapus, kemudian isi baru

dimasukkan. Caret akan ditempatkan pada awal teks. Jika teks berisi

pemisah baris (misalnya "\n") pada kontrol dengan bit gaya

SWT.SINGLE hasilnya tidak akan sesuai dengan yang diharapkan.

Karakter mnemonik tidak didukung pada kontrol Text

getText() Mengambil String yang merupakan isi teks

getText(int

mulai, int

akhir)

Mengambil String yang merupakan isi teks dimulai dari karakter

ke-mulai hingga karakter ke-akhir, termasuk karakter ke-mulai dan

karakter ke-akhir. Indeks dimulai dari nol. Jika indeks berada di luar

jangkauan, SWT akan mencoba mengambil karakter sebanyak

Page 481: Java With Eclipse

mungkin.

getCharCount Mengembalikan int yang merupakan jumlah karakter pada kontrol.

Sebagian string dalam kontrol teks dapat dapat dipilih dengan menggunakan metode

setSelection(). Metode ini mengambil argumen berupa posisi caret awal dan posisi

caret akhir pilihan. Berikut ini adalah ilustrasi posisi caret di dalam suatu kontrol Text

Metode Penjelasan

setSelection(int

caretAwal, int

caretAkhir)

Mengeset pilihan pada kontrol dari posisi caretAwal ke

posisi caretAkhir. Jika caretAwal dan caretAkhir bernilai

sama, maka caret akan berubah menjadi kursor di posisi

tersebut.

setSelection(int

caretAwal)

Sama dengan memanggil dengan

setSelection(caretAwal, caretAwal)

selectAll()

Memilih semua string pada kontrol text. Sama dengan

memanggil dengan setSelection(0,

teks1.getCharCount())

clearSelection()

Menghapus pilihan dan memindahkan kursor ke posisi

pilihan pertama. (Misalnya, teks "hello" dipilih sebelumnya,

setelah metode ini dipanggil, posisi kursor akan berpindah

ke sebelum "h")

getSelection()

Mengembalikan Point yang merupakan posisi pilihan.

Point.x yang dikembalikan adalah posisi caretAwal dan

Point.y adalah posisi caretAkhir.

getSelectionCount()Mengembalikan int yaitu jumlah karakter yang ada di

dalam pilihan

Program berikut membuat input teks banyak baris, yang kemudian memilih kata

"Indonesia", seperti pada gambar berikut.

Page 482: Java With Eclipse

package com.lyracc.pilihteks;

import org.eclipse.swt.*;

import org.eclipse.swt.widgets.*;

import org.eclipse.swt.layout.*;

public class PilihTeks {

// Variabel-variabel widget kita deklarasikan sebagai

// variabel privat

private Display display;

private Shell shell;

private Text teks1;

// Konstruktor kelas ini, untuk menginisialisasi semua widget

PilihTeks() {

// Membuat display dan shell baru

display = new Display();

shell = new Shell(display);

shell.setText("Hello SWT");

shell.setSize(200,250);

shell.setLayout(new FillLayout());

// Inisialisasi label

teks1 = new Text(shell, SWT.BORDER | SWT.MULTI |

SWT.WRAP | SWT.V_SCROLL | SWT.H_SCROLL);

teks1.setText("Kita bersama-sama di sini, untuk menegaskan " +

"kembali Indonesia tempat kita berdiri.");

teks1.setSelection(52, 61);

}

Page 483: Java With Eclipse

// Perintah "standar" SWT, harus ada pada setiap aplikasi SWT

public void run() {

shell.open();

while (!shell.isDisposed())

if (!display.readAndDispatch())

display.sleep();

display.dispose();

}

/**

* @param args

*/

public static void main(String[] args) {

// Instansiasi kelas ini, kemudian jalankan run

PilihTeks jendela = new PilihTeks();

jendela.run();

}

}

Menyisipkan dan Menambah Teks

Karakter bisa disisipkan ke dalam kontrol teks dengan metode insert() atau bisa

ditambahkan dengan metode append(). Penyisipan dilakukan pada posisi kursor,

sedangkan penambahan teks selalu dilakukan di akhir teks. Untuk menyisipkan teks pada

posisi tertentu, pindahkan dahulu posisi caret ke posisi yang diinginkan dengan

setSelection(), baru lakukan penyisipan.

Metode Penjelasan

insert(String s) Menyisipkan String s pada posisi caret.

append(String s) Menambahkan String s di akhir input teks

Jika sebagian teks sudah dipilih, maka teks yang dipilih akan dihapus, kemudian teks baru

disisipkan di posisi tersebut.

Operasi Clipboard

Kontrol Text menyediakan fasilitas copy, cut, dan paste ke clipboard. Kontrol ini juga

mendukung tombol shortcut (misalnya Ctrl-C, Ctrl-X, dan Ctrl-V) untuk melakukan

operasi clipboard dari dan ke kontrol ini. Beberapa platform juga menyediakan menu

konteks (yang keluar ketika kita mengklik kanan di kontrol tersebut) yang berisi operasi

clipboard ini.

Page 484: Java With Eclipse

Metode Penjelasan

cut() Teks yang dipilih di-cut ke dalam clipboard

copy() Teks yang dipilih di-copy ke dalam clipboard

paste()

Menyisipkan karakter dari dalam clipboard di posisi caret. Jika caret sudah

memilih sebagian teks, teks dipilih akan dihapus dan diganti dengan teks dari

clipboard

Membatasi Jumlah Karakter yang Bisa Diinput

Untuk beberapa aplikasi, kadang diperlukan batas jumlah karakter yang diinput oleh user.

Misalnya, dalam aplikasi database, beberapa tabel hanya bisa menerima input sejumlah

karakter saja. Untuk itu, aplikasi perlu membatasi jumlah karakter yang bisa diinput oleh

user.

Metode Penjelasan

setTextLimit(int

batas)

Mengeset maksimum jumlah karakter yang bisa dimasukkan ke

dalam input teks

getTextLimit()Mengembalikan int yang merupakan batas maksimum jumlah

karakter yang bisa dimasukkan ke dalam input teks

Password dan Karakter Echo

Untuk membuat input teks yang disamarkan, seperti untuk field password, kita bisa

menggunakan bit gaya SWT.PASSWORD. Ketika user mengetikkan karakter pada input teks

ini, karakter yang diinput oleh user akan disamarkan dengan suatu karakter, misalnya (*)

atau (.). Kita bisa mengeset karakter apa yang digunakan untuk menyamarkan inputnya.

Karakter ini disebut karakter echo.

Metode Penjelasan

setEchoCharacter(char

c)Mengeset karakter echo dengan karakter tertentu

getEchoCharacter()Mengembalikan char yang merupakan karakter echo dari

input teks password

Bekerja Dengan Input Teks Banyak Baris

Input teks banyak baris memiliki fitur tambahan dibandingkan dengan input teks satu baris.

Salah satunya, karena inputnya bisa terdiri dari beberapa baris, kita bisa mengeset

Page 485: Java With Eclipse

bagaimana teks akan ditampilkan. Jika kita menggunakan SWT.WRAP, maka teks yang

panjang akan otomatis dipotong sehingga akan ditampilkan dalam beberapa baris.

Kita juga bisa mengeset karakter apa yang digunakan sebagai pembatas baris. Setiap

sistem operasi memiliki pembatas baris yang mungkin berbeda-beda. (Misalnya pada

Windows "\n\r", pada Linux "\n", dan pada Macintosh "\r"). KIta bisa mengubah pembatas

barisnya, akan tetapi mungkin aplikasi yang kita buat sulit bekerja lintas platform.

Variabel statik DELIMITER pada kelas Text digunakan untuk mengetahui pembatas baris

pada suatu platform. Ketika kita membaca input teks dari user, sistem operasi akan

otomatis menambahkan pembatas baris. Untuk itu kadang kita perlu mengetahui pembatas

barisnya, sehingga input dari user bisa kita olah lebih tepat. Metode lain yang bisa

digunakan adalah getLineDelimiter().

Menggulung Teks

Input teks banyak baris membutuhkan scroll bar baik horizontal dan vertikal. Kadang-

kadang kita perlu menggulung layar secara otomatis (supaya user tidak perlu lagi

menggulung layar sendiri, misalnya jika teks yang kita berikan sangat panjang dan kita

hanya tertarik pada bagian akhir teks).

Metode Penjelasan

setTopIndex(int

i)

Menggulung layar sehingga baris ke-i ditampilkan di baris pertama

input teks

getTopIndex()Mengembalikan int yang merupakan baris pertama input teks yang

ditampilkan.

showSelection()

Jika input teks berupa input teks banyak baris, dan pilihan tidak

ditunjukkan, misalnya karena tersembunyi di bagian bawah teks yang

panjang, setelah memanggil showSelection(), maka teks yang

dipilih akan otomatis ditunjukkan, yaitu dengan menggeser scroll bar

secara otomatis hingga pilihan terlihat