
-
یک رابط فقط رفتار را توصیف می کند. حالت ندارد. اما یک کلاس انتزاعی شامل حالت است: هر دو را توصیف می کند.
به عنوان مثال،
Bird
کلاس انتزاعی وCanFly
رابط را در نظر بگیرید: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; } }
بیایید یک
MockingJay
کلاس پرنده ایجاد کنیم و آن را به ارث ببریمBird
: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()); } }
همانطور که می بینید، ما به راحتی می توانیم به وضعیت کلاس انتزاعی - آن
species
وage
متغیرها دسترسی پیدا کنیم.اما اگر بخواهیم همین کار را با یک رابط انجام دهیم، تصویر متفاوت است. می توانیم سعی کنیم متغیرهایی را به آن اضافه کنیم:
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(); }
ما حتی نمی توانیم متغیرهای خصوصی را در داخل یک رابط اعلام کنیم. چرا؟ زیرا اصلاح کننده خصوصی برای پنهان کردن پیاده سازی از کاربر ایجاد شده است. و یک رابط هیچ پیاده سازی در داخل آن ندارد: چیزی برای پنهان کردن وجود ندارد.
یک رابط فقط رفتار را توصیف می کند. بر این اساس، ما نمی توانیم دریافت کننده ها و تنظیم کننده ها را در داخل یک رابط پیاده سازی کنیم. این ماهیت رابط ها است: آنها برای کار با رفتار مورد نیاز هستند، نه حالت.
جاوا 8 روش های پیش فرضی را برای رابط هایی که پیاده سازی دارند معرفی کرد. شما قبلاً در مورد آنها می دانید، بنابراین ما خودمان را تکرار نمی کنیم.
-
یک کلاس انتزاعی کلاس هایی را که بسیار نزدیک به هم مرتبط هستند را به هم متصل و متحد می کند. در عین حال، یک رابط واحد را می توان توسط کلاس هایی که مطلقاً هیچ اشتراکی ندارند، پیاده سازی کرد.
بیایید به مثال خود در مورد پرندگان بازگردیم.
کلاس انتزاعی ما
Bird
برای ایجاد پرندگانی که بر اساس آن کلاس هستند مورد نیاز است. فقط پرنده و دیگر هیچ! البته انواع مختلفی از پرندگان وجود خواهد داشت.با
CanFly
رابط، هر کس به روش خود کار می کند. این فقط رفتار (پرواز) مرتبط با نام آن را توصیف می کند. بسیاری از چیزهای نامرتبط "می توانند پرواز کنند".این 4 موجودیت به یکدیگر مرتبط نیستند. حتی همه آنها زنده نیستند. با این حال، همه آنها
CanFly
.ما نتوانستیم آنها را با استفاده از یک کلاس انتزاعی توصیف کنیم. آنها حالت یکسان یا زمینه های یکسانی ندارند. برای تعریف یک هواپیما، احتمالاً به زمینه هایی برای مدل، سال تولید و حداکثر تعداد مسافر نیاز داریم. برای کارلسون، ما برای همه شیرینیهایی که امروز خورد و فهرستی از بازیهایی که با برادر کوچکش انجام خواهد داد، به زمینهایی نیاز داریم. برای یک پشه، ... اوه ... من حتی نمی دانم ... شاید، یک "سطح آزار"؟ :)
نکته این است که ما نمی توانیم از یک کلاس انتزاعی برای توصیف آنها استفاده کنیم. آنها خیلی متفاوت هستند. اما آنها رفتار مشترکی دارند: آنها می توانند پرواز کنند. یک رابط برای توصیف هر چیزی در جهان که می تواند پرواز کند، شنا کند، بپرد یا رفتار دیگری از خود نشان دهد، عالی است.
-
کلاس ها می توانند هر تعداد واسط که بخواهید پیاده سازی کنند، اما فقط می توانند یک کلاس را به ارث ببرند.
ما قبلاً بیش از یک بار به این موضوع اشاره کرده ایم. جاوا وراثت چندگانه کلاس ها را ندارد، اما از وراثت چندگانه رابط ها پشتیبانی می کند. این نکته تا حدی از مورد قبلی پیروی می کند: یک رابط کلاس های مختلف زیادی را که اغلب هیچ چیز مشترک دیگری ندارند، به هم متصل می کند، در حالی که یک کلاس انتزاعی برای گروهی از کلاس های بسیار نزدیک ایجاد می شود. بنابراین، منطقی است که شما فقط می توانید یک کلاس را به ارث ببرید. یک کلاس انتزاعی رابطه "is-a" را توصیف می کند.
رابط های استاندارد: InputStream و OutputStream
ما قبلاً کلاسهای مختلفی را که مسئول جریانهای ورودی و خروجی هستند بررسی کردهایم. بیایید در نظر بگیریمInputStream
و OutputStream
. به طور کلی، اینها اصلاً رابط نیستند، بلکه کلاس های انتزاعی کاملاً واقعی هستند. اکنون می دانید که این به چه معناست، بنابراین کار با آنها بسیار آسان تر خواهد بود :) InputStream
یک کلاس انتزاعی است که مسئول ورودی بایت است. جاوا چندین کلاس دارد که ارث می برند InputStream
. هر یک از آنها برای دریافت داده ها از منابع مختلف طراحی شده اند. از آنجایی که InputStream
والد است، چندین روش ارائه می دهد که کار با جریان های داده را آسان می کند. هر یک از فرزندان InputStream
این روش ها را دارند:
int available()
تعداد بایت های موجود برای خواندن را برمی گرداند.close()
جریان ورودی را می بندد.int read()
یک نمایش عدد صحیح از بایت بعدی موجود در جریان را برمیگرداند. اگر به انتهای جریان رسیده باشد، -1 برگردانده خواهد شد.int read(byte[] buffer)
سعی می کند بایت ها را در بافر بخواند و تعداد بایت های خوانده شده را برمی گرداند. وقتی به انتهای فایل رسید، -1 را برمی گرداند.int read(byte[] buffer, int byteOffset, int byteCount)
بخشی از یک بلوک بایت را می نویسد. زمانی استفاده می شود که ممکن است آرایه بایت به طور کامل پر نشده باشد. وقتی به انتهای فایل رسید، -1 را برمی گرداند.long skip(long byteCount)
byteCount بایت ها را در جریان ورودی رد می کند و تعداد بایت های نادیده گرفته شده را برمی گرداند.
FileInputStream
: رایج ترین نوعInputStream
. برای خواندن اطلاعات از یک فایل استفاده می شود.StringBufferInputStream
: نوع مفید دیگری ازInputStream
. یک رشته را به یک تبدیل می کندInputStream
.BufferedInputStream
: یک جریان ورودی بافر. اغلب برای افزایش عملکرد استفاده می شود.
BufferedReader
و گفتیم که لازم نیست ازش استفاده کنی؟ وقتی می نویسیم:
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in))
... لازم نیست از BufferedReader
: یک InputStreamReader
می تواند کار را انجام دهد استفاده کنید. اما BufferedReader
عملکرد را بهبود می بخشد و همچنین می تواند خطوط کامل داده را به جای کاراکترهای فردی بخواند. همین مورد در مورد هم صدق می کند BufferedInputStream
! کلاس داده های ورودی را بدون دسترسی مداوم به دستگاه ورودی در یک بافر خاص جمع می کند. بیایید یک مثال را در نظر بگیریم:
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();
}
}
}
در این مثال، دادهها را از فایلی میخوانیم که در رایانه در آدرس ' D:/Users/UserName/someFile.txt ' قرار دارد. ما 2 شی ایجاد می کنیم - یک FileInputStream
و یک BufferedInputStream
که آن را می پیچد. سپس بایت های فایل را می خوانیم و آنها را به کاراکتر تبدیل می کنیم. و این کار را تا پایان فایل انجام می دهیم. همانطور که می بینید، هیچ چیز پیچیده ای در اینجا وجود ندارد. می توانید این کد را کپی کرده و روی یک فایل واقعی در رایانه خود اجرا کنید :) OutputStream
کلاس یک کلاس انتزاعی است که یک جریان خروجی از بایت ها را نشان می دهد. همانطور که می دانید، این برعکس یک است InputStream
. مسئول خواندن داده ها از جایی نیست، بلکه مسئول ارسال داده به جایی است . مانند InputStream
، این کلاس انتزاعی به همه فرزندان خود مجموعه ای از روش های راحت می دهد:
void close()
جریان خروجی را می بندد.void flush()
تمام بافرهای خروجی را پاک می کند.abstract void write(int oneByte)
1 بایت را در جریان خروجی می نویسد.void write(byte[] buffer)
یک آرایه بایت در جریان خروجی می نویسد.void write(byte[] buffer, int offset, int count)
محدوده ای از تعداد بایت ها را از یک آرایه می نویسد که از موقعیت افست شروع می شود.
OutputStream
کلاس آورده شده است:
-
DataOutputStream
. یک جریان خروجی که شامل روش هایی برای نوشتن انواع داده های استاندارد جاوا است.یک کلاس بسیار ساده برای نوشتن انواع داده ها و رشته های جاوا اولیه. احتمالاً کد زیر را حتی بدون توضیح هم متوجه خواهید شد:
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); } }
برای هر نوع روش های جداگانه ای دارد -
writeDouble()
،،، و غیره.writeLong()
writeShort()
FileOutputStream
. این کلاس مکانیزمی را برای ارسال داده به فایل روی دیسک پیاده سازی می کند. به هر حال، ما قبلاً از آن در آخرین مثال استفاده کردیم. متوجه شدید؟ ما آن را به DataOutputStream ارسال کردیم، که به عنوان یک "پوشش" عمل می کرد.BufferedOutputStream
. یک جریان خروجی بافر. همچنین هیچ چیز پیچیده ای در اینجا وجود ندارد. هدف آن مشابهBufferedInputStream
(یاBufferedReader
) است. به جای خواندن متوالی معمول داده ها، داده ها را با استفاده از یک بافر تجمعی خاص می نویسد. بافر امکان کاهش تعداد دفعات دسترسی به سینک داده را فراهم می کند و در نتیجه عملکرد را افزایش می دهد.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); } }
باز هم، می توانید خودتان با این کد بازی کنید و بررسی کنید که روی فایل های واقعی روی رایانه شما کار می کند.
FileInputStream
ما یک درس جداگانه در مورد ، FileOutputStream
و خواهیم داشت BuffreredInputStream
، بنابراین این اطلاعات برای اولین آشنایی کافی است. خودشه! امیدواریم تفاوت بین رابط ها و کلاس های انتزاعی را درک کرده باشید و آماده پاسخگویی به هر سوالی، حتی سوالات ترفندی باشید :)
GO TO FULL VERSION