
-
Antarmuka hanya menjelaskan perilaku. Ia tidak memiliki keadaan. Tetapi kelas abstrak menyertakan status: ini menjelaskan keduanya.
Misalnya, ambil
Bird
kelas abstrak danCanFly
antarmuka:public abstract class Bird { private String species; private int age; public abstract void fly(); public String getSpecies() { return species; } public void setSpecies(String species) { this.species = species; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
Mari buat
MockingJay
kelas burung dan buat itu inheritBird
:public class MockingJay extends Bird { @Override public void fly() { System.out.println("Fly, bird!"); } public static void main(String[] args) { MockingJay someBird = new MockingJay(); someBird.setAge(19); System.out.println(someBird.getAge()); } }
Seperti yang Anda lihat, kita dapat dengan mudah mengakses status kelas abstrak —
species
danage
variabelnya.Tetapi jika kami mencoba melakukan hal yang sama dengan antarmuka, gambarnya berbeda. Kita dapat mencoba menambahkan variabel ke dalamnya:
public interface CanFly { String species = new String(); int age = 10; public void fly(); } public interface CanFly { private String species = new String(); // Error private int age = 10; // Another error public void fly(); }
Kami bahkan tidak dapat mendeklarasikan variabel pribadi di dalam antarmuka. Mengapa? Karena pengubah pribadi dibuat untuk menyembunyikan implementasi dari pengguna. Dan sebuah antarmuka tidak memiliki implementasi di dalamnya: tidak ada yang disembunyikan.
Antarmuka hanya menjelaskan perilaku. Karenanya, kami tidak dapat mengimplementasikan getter dan setter di dalam antarmuka. Ini adalah sifat antarmuka: mereka diperlukan untuk bekerja dengan perilaku, bukan keadaan.
Java 8 memperkenalkan metode default untuk antarmuka yang memiliki implementasi. Anda sudah tahu tentang mereka, jadi kami tidak akan mengulanginya.
-
Kelas abstrak menghubungkan dan menyatukan kelas-kelas yang sangat erat kaitannya. Pada saat yang sama, satu antarmuka dapat diimplementasikan oleh kelas yang sama sekali tidak memiliki kesamaan.
Mari kembali ke contoh kita dengan burung.
Kelas abstrak kita
Bird
diperlukan untuk membuat burung berdasarkan kelas tersebut. Hanya burung dan tidak ada yang lain! Tentu saja, akan ada berbagai jenis burung.Dengan
CanFly
antarmuka, setiap orang dapat melanjutkan dengan caranya sendiri. Itu hanya menggambarkan perilaku (terbang) yang terkait dengan namanya. Banyak hal yang tidak berhubungan 'bisa terbang'.4 entitas ini tidak berhubungan satu sama lain. Mereka bahkan tidak semuanya hidup. Namun, mereka semua
CanFly
.Kami tidak dapat menggambarkannya menggunakan kelas abstrak. Mereka tidak berbagi negara bagian yang sama atau bidang yang identik. Untuk mendefinisikan sebuah pesawat, kita mungkin membutuhkan kolom untuk model, tahun produksi, dan jumlah maksimum penumpang. Untuk Carlson, kami membutuhkan ladang untuk semua permen yang dia makan hari ini, dan daftar permainan yang akan dia mainkan dengan adik laki-lakinya. Untuk seekor nyamuk, ...uh... Aku bahkan tidak tahu... Mungkin, 'tingkat gangguan'? :)
Intinya adalah kita tidak bisa menggunakan kelas abstrak untuk mendeskripsikannya. Mereka terlalu berbeda. Tapi mereka memiliki perilaku yang sama: mereka bisa terbang. Antarmuka sangat cocok untuk menggambarkan segala sesuatu di dunia yang dapat terbang, berenang, melompat, atau menunjukkan perilaku lainnya.
-
Kelas dapat mengimplementasikan antarmuka sebanyak yang Anda inginkan, tetapi mereka hanya dapat mewarisi satu kelas.
Kami telah menyebutkan ini lebih dari sekali. Java tidak memiliki banyak pewarisan kelas, tetapi Java mendukung banyak pewarisan antarmuka. Poin ini sebagian mengikuti dari yang sebelumnya: sebuah antarmuka menghubungkan banyak kelas berbeda yang seringkali tidak memiliki kesamaan, sementara kelas abstrak dibuat untuk sekelompok kelas yang sangat dekat hubungannya. Oleh karena itu, masuk akal jika Anda hanya dapat mewarisi satu kelas tersebut. Kelas abstrak menggambarkan hubungan 'is-a'.
Antarmuka standar: InputStream dan OutputStream
Kami telah membahas berbagai kelas yang bertanggung jawab atas input dan output stream. Mari kita pertimbangkanInputStream
dan OutputStream
. Secara umum, ini sama sekali bukan antarmuka, melainkan kelas abstrak yang sepenuhnya asli. Sekarang Anda tahu artinya, jadi akan lebih mudah untuk bekerja dengan mereka :) InputStream
adalah kelas abstrak yang bertanggung jawab atas input byte. Java memiliki beberapa kelas yang mewarisi InputStream
. Masing-masing dirancang untuk menerima data dari sumber yang berbeda. Karena InputStream
induknya, ia menyediakan beberapa metode yang memudahkan bekerja dengan aliran data. Setiap keturunan InputStream
memiliki metode ini:
int available()
mengembalikan jumlah byte yang tersedia untuk dibaca;close()
menutup aliran input;int read()
mengembalikan representasi bilangan bulat dari byte berikutnya yang tersedia dalam aliran. Jika akhir aliran telah tercapai, -1 akan dikembalikan;int read(byte[] buffer)
mencoba membaca byte ke dalam buffer, dan mengembalikan jumlah byte yang dibaca. Saat mencapai akhir file, ia mengembalikan -1;int read(byte[] buffer, int byteOffset, int byteCount)
menulis bagian dari blok byte. Ini digunakan ketika array byte mungkin belum terisi seluruhnya. Saat mencapai akhir file, ia mengembalikan -1;long skip(long byteCount)
melompati byteCount byte dalam aliran input, dan mengembalikan jumlah byte yang diabaikan.
FileInputStream
: jenis yang paling umum dariInputStream
. Ini digunakan untuk membaca informasi dari file;StringBufferInputStream
: JenisInputStream
. Itu mengubah string menjadiInputStream
;BufferedInputStream
: Aliran input yang di-buffer. Ini paling sering digunakan untuk meningkatkan kinerja.
BufferedReader
dan mengatakan bahwa Anda tidak harus menggunakannya? Ketika kita menulis:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
… Anda tidak harus menggunakan BufferedReader
: An InputStreamReader
can do the job. Tetapi BufferedReader
meningkatkan kinerja dan juga dapat membaca seluruh baris data daripada karakter individual. Hal yang sama berlaku untuk BufferedInputStream
! Kelas mengumpulkan data input dalam buffer khusus tanpa terus-menerus mengakses perangkat input. Mari kita pertimbangkan sebuah contoh:
import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.InputStream;
public class BufferedInputExample {
public static void main(String[] args) throws Exception {
InputStream inputStream = null;
BufferedInputStream buffer = null;
try {
inputStream = new FileInputStream("D:/Users/UserName/someFile.txt");
buffer = new BufferedInputStream(inputStream);
while(buffer.available()>0) {
char c = (char)buffer.read();
System.out.println("Character read: " + c);
}
} catch(Exception e) {
e.printStackTrace();
} finally {
inputStream.close();
buffer.close();
}
}
}
Dalam contoh ini, kami membaca data dari file yang terletak di komputer di ' D:/Users/UserName/someFile.txt '. Kami membuat 2 objek — a FileInputStream
dan a BufferedInputStream
yang 'membungkusnya'. Kemudian kami membaca byte dari file dan mengubahnya menjadi karakter. Dan kami melakukan itu sampai file berakhir. Seperti yang Anda lihat, tidak ada yang rumit di sini. Anda dapat menyalin kode ini dan menjalankannya pada file asli di komputer Anda :) Kelas OutputStream
adalah kelas abstrak yang mewakili aliran keluaran byte. Seperti yang sudah Anda ketahui, ini adalah kebalikan dari file InputStream
. Itu tidak bertanggung jawab untuk membaca data dari suatu tempat, melainkan untuk mengirim data ke suatu tempat . Seperti InputStream
, kelas abstrak ini memberikan semua keturunannya satu set metode mudah:
void close()
menutup aliran keluaran;void flush()
membersihkan semua buffer keluaran;abstract void write(int oneByte)
menulis 1 byte ke aliran keluaran;void write(byte[] buffer)
menulis array byte ke aliran keluaran;void write(byte[] buffer, int offset, int count)
menulis rentang hitungan byte dari array, mulai dari posisi offset.
OutputStream
:
-
DataOutputStream
. Aliran output yang menyertakan metode untuk menulis tipe data Java standar.Kelas yang sangat sederhana untuk menulis tipe dan string data Java primitif. Anda mungkin akan memahami kode berikut bahkan tanpa penjelasan:
import java.io.*; public class DataOutputStreamExample { public static void main(String[] args) throws IOException { DataOutputStream dos = new DataOutputStream(new FileOutputStream("testFile.txt")); dos.writeUTF("SomeString"); dos.writeInt(22); dos.writeDouble(1.21323); dos.writeBoolean(true); } }
Ini memiliki metode terpisah untuk setiap jenis —
writeDouble()
,writeLong()
,writeShort()
, dan seterusnya. FileOutputStream
. Kelas ini menerapkan mekanisme pengiriman data ke file di disk. Omong-omong, kami sudah menggunakannya di contoh terakhir. Apakah kamu menyadari? Kami meneruskannya ke DataOutputStream, yang bertindak sebagai 'pembungkus'.BufferedOutputStream
. Aliran keluaran yang di-buffer. Juga tidak ada yang rumit di sini. Tujuannya analog denganBufferedInputStream
(atauBufferedReader
). Alih-alih membaca data berurutan seperti biasa, ia menulis data menggunakan buffer 'kumulatif' khusus. Buffer memungkinkan untuk mengurangi berapa kali data sink diakses, sehingga meningkatkan kinerja.import java.io.*; public class DataOutputStreamExample { public static void main(String[] args) throws IOException { FileOutputStream outputStream = new FileOutputStream("D:/Users/Username/someFile.txt"); BufferedOutputStream bufferedStream = new BufferedOutputStream(outputStream); String text = "I love Java!"; // We'll convert this string to a byte array and write it to a file byte[] buffer = text.getBytes(); bufferedStream.write(buffer, 0, buffer.length); } }
Sekali lagi, Anda dapat bermain-main dengan kode ini sendiri dan memverifikasi bahwa itu akan berfungsi pada file asli di komputer Anda.
FileInputStream
, FileOutputStream
dan BuffreredInputStream
, jadi ini adalah informasi yang cukup untuk kenalan pertama. Itu dia! Kami harap Anda memahami perbedaan antara antarmuka dan kelas abstrak dan siap menjawab pertanyaan apa pun, bahkan pertanyaan jebakan :)
GO TO FULL VERSION