JavaRush /وبلاگ جاوا /Random-FA /تفاوت بین کلاس های انتزاعی و رابط ها

تفاوت بین کلاس های انتزاعی و رابط ها

در گروه منتشر شد
سلام! در این سخنرانی، ما در مورد تفاوت کلاس های انتزاعی با رابط ها صحبت خواهیم کرد و به مثال هایی با کلاس های انتزاعی رایج نگاه خواهیم کرد. تفاوت بین کلاس های انتزاعی و رابط ها - 1ما یک سخنرانی جداگانه را به تفاوت های بین کلاس انتزاعی و رابط اختصاص دادیم، زیرا موضوع بسیار مهم است. از شما در مورد تفاوت این مفاهیم در 90 درصد مصاحبه های بعدی سوال می شود. بنابراین، حتماً مطالبی را که می‌خوانید متوجه شده باشید و اگر چیزی را کاملاً متوجه نشدید، منابع اضافی را مطالعه کنید. بنابراین، ما می دانیم که کلاس انتزاعی چیست و رابط چیست. حالا بیایید به تفاوت های آنها بپردازیم.
  1. یک رابط فقط رفتار را توصیف می کند. او هیچ ثروتی ندارد. اما یک کلاس انتزاعی حالتی دارد: هر دو را توصیف می کند.

    بیایید یک کلاس انتزاعی Birdو رابط را به عنوان مثال در نظر بگیریم Flyable:

    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(mockingjay) ایجاد کنیم و از Bird:

    public class Mockingjay extends Bird {
    
       @Override
       public void fly() {
           System.out.println("Fly, birdie!");
       }
    
       public static void main(String[] args) {
    
           Mockingjay someBird = new Mockingjay();
           someBird.setAge(19);
           System.out.println(someBird.getAge());
       }
    }

    همانطور که می بینید، ما به راحتی می توانیم به وضعیت کلاس انتزاعی - متغیرهای آن species(نوع) و age(سن) دسترسی پیدا کنیم.

    اما اگر بخواهیم همین کار را با رابط انجام دهیم، تصویر متفاوت خواهد بود. می توانیم متغیرهایی را به آن اضافه کنیم:

    public interface Flyable {
    
       String species = new String();
       int age = 10;
    
       public void fly();
    }
    
    public interface Flyable {
    
       private String species = new String(); // error
       private int age = 10; // also an error
    
       public void fly();
    }

    ما حتی قادر به ایجاد متغیرهای خصوصی در داخل رابط نخواهیم بود . چرا؟ زیرا اصلاح کننده خصوصی برای پنهان کردن پیاده سازی از کاربر ایجاد شده است. اما هیچ پیاده سازی در داخل رابط وجود ندارد: چیزی برای پنهان کردن وجود ندارد.

    رابط فقط رفتار را توصیف می کند. بر این اساس، نمی‌توانیم گیرنده‌ها و تنظیم‌کننده‌ها را در داخل اینترفیس پیاده‌سازی کنیم. این ماهیت یک رابط است: به معنای برخورد با رفتار است، نه حالت.

    Java8 روش های رابط پیش فرضی را معرفی کرد که پیاده سازی دارند. شما قبلاً در مورد آنها می دانید، بنابراین ما آنها را تکرار نمی کنیم.

  2. یک کلاس انتزاعی کلاس هایی را که رابطه بسیار نزدیکی دارند پیوند می دهد و یکی می کند. در عین حال، همان رابط را می توان توسط کلاس هایی پیاده سازی کرد که هیچ وجه اشتراکی ندارند.

    بیایید به مثال خود در مورد پرندگان بازگردیم.

    کلاس انتزاعی ما Birdبرای ایجاد پرندگان بر اساس آن مورد نیاز است. فقط پرندگان و هیچ کس دیگری! البته آنها متفاوت خواهند بود.

    تفاوت بین کلاس های انتزاعی و رابط ها - 2

    با رابط Flyableهمه چیز متفاوت است. این فقط رفتار مربوط به نام آن - "پرواز" را توصیف می کند. تعریف "پرواز"، "قابلیت پرواز" شامل بسیاری از اشیاء است که به یکدیگر مرتبط نیستند.

    تفاوت بین کلاس های انتزاعی و رابط ها - 3

    این 4 موجودیت به هیچ وجه با یکدیگر مرتبط نیستند. چه بگویم، همه آنها حتی جاندار نیستند. با این حال، همه آنها Flyableقادر به پرواز هستند.

    ما نمی توانیم آنها را با استفاده از یک کلاس انتزاعی توصیف کنیم. آنها حالت مشترک یا زمینه های یکسانی ندارند. برای مشخص کردن یک هواپیما، احتمالاً به فیلدهای «مدل»، «سال ساخت» و «حداکثر تعداد مسافر» نیاز داریم. برای کارلسون، زمین‌هایی برای همه شیرینی‌هایی که امروز خورد و فهرستی از بازی‌هایی که با بچه بازی خواهد کرد وجود دارد. برای یک پشه ... اوه - اوه ... ما حتی نمی دانیم ... شاید "سطح آزار"؟ :)

    نکته اصلی این است که ما نمی توانیم آنها را با استفاده از یک کلاس انتزاعی توصیف کنیم. آنها خیلی متفاوت هستند. اما یک رفتار مشترک وجود دارد: آنها می توانند پرواز کنند. رابط کاربری ایده آل برای توصیف هر چیزی در جهان است که می تواند پرواز کند، شنا کند، بپرد یا رفتار دیگری داشته باشد.

  3. کلاس ها می توانند هر تعداد رابط را که بخواهند پیاده سازی کنند، اما فقط می توانند از یک کلاس ارث بری کنند.

    ما قبلاً بیش از یک بار در مورد این موضوع صحبت کرده ایم. در جاوا وراثت چندگانه وجود ندارد، اما پیاده سازی چندگانه وجود دارد. این نکته تا حدی از مورد قبلی پیروی می کند: یک اینترفیس کلاس های مختلفی را که اغلب هیچ وجه اشتراکی ندارند به هم متصل می کند و یک کلاس انتزاعی برای گروهی از کلاس ها ایجاد می شود که بسیار نزدیک به یکدیگر هستند. بنابراین، منطقی است که شما فقط از یک کلاس از این قبیل ارث ببرید. یک کلاس انتزاعی رابطه "است" را توصیف می کند.

رابط های جریان ورودی و خروجی استاندارد

ما قبلاً از کلاس های مختلف مسئول ورودی و خروجی جریان عبور کرده ایم. بیایید نگاه کنیم 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)skips byteCount، یک بایت ورودی، تعداد بایت های نادیده گرفته شده را برمی گرداند.
من به شما توصیه می کنم لیست کامل روش ها را مطالعه کنید . در واقع بیش از دوازده کلاس جانشین وجود دارد. در اینجا چند نمونه هستند:
  1. FileInputStream: رایج ترین نوع InputStream. برای خواندن اطلاعات از یک فایل استفاده می شود.
  2. StringBufferInputStreamInputStream: یک نوع مفید دیگر یک رشته را به یک جریان داده ورودی تبدیل می کند InputStream.
  3. 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 was read" + c);
           }
       } catch(Exception e) {

           e.printStackTrace();

       } finally {

           inputStream.close();
           buffer.close();
       }
   }
}
در این مثال، ما در حال خواندن داده‌ها از فایلی هستیم که در رایانه در آدرس "D:/Users/UserName/someFile.txt" قرار دارد . ما 2 شی ایجاد می کنیم - FileInputStreamو BufferedInputStreamبه عنوان "لفاف بندی" آن. پس از آن، بایت های فایل را می خوانیم و آنها را به کاراکتر تبدیل می کنیم. و به همین ترتیب تا زمانی که فایل تمام شود. همانطور که می بینید، هیچ چیز پیچیده ای در اینجا وجود ندارد. شما می توانید این کد را کپی کرده و آن را بر روی برخی از فایل های واقعی که در رایانه شما ذخیره شده است اجرا کنید :) کلاس یک OutputStreamکلاس انتزاعی است که خروجی جریان بایت را تعریف می کند. همانطور که قبلاً فهمیدید، این نقطه مقابل InputStream«a» است. مسئول این نیست که داده ها را از کجا بخواند، بلکه مسئول مکان ارسال آن است . مانند InputStream، این کلاس انتزاعی گروهی از متدها را برای کار راحت به همه فرزندان ارائه می دهد:
  • int close()جریان خروجی را می بندد.
  • void flush()تمام بافرهای خروجی را پاک می کند.
  • abstract void write (int oneByte)1 بایت را در جریان خروجی می نویسد.
  • void write (byte[] buffer)آرایه ای از بایت ها را در جریان خروجی می نویسد.
  • void write (byte[] buffer, int offset, int count)محدوده ای از تعداد بایت ها را از آرایه می نویسد، که از موقعیت افست شروع می شود.
در اینجا برخی از فرزندان این کلاس آورده شده است OutputStream:
  1. 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()غیره.

  2. کلاس FileOutputStream . مکانیزمی را برای ارسال داده ها به فایل روی دیسک پیاده سازی می کند. به هر حال، قبلاً در مثال قبلی از آن استفاده کردیم، آیا متوجه شدید؟ ما آن را در داخل DataOutputStream، که به عنوان یک "پوشش" عمل می کرد، عبور دادیم.

  3. 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 will convert this string into an array of bytes and write it to a file
    
           byte[] buffer = text.getBytes();
    
           bufferedStream.write(buffer, 0, buffer.length);
           bufferedStream.close();
       }
    }

    باز هم، می توانید خودتان با این کد «بازی» کنید و بررسی کنید که چگونه روی فایل های واقعی روی رایانه شما کار می کند.

همچنین می توانید در مورد ورثه در ماده " سیستم InputStreamورودی / خروجی " بخوانید. اوه ، و ما همچنین یک سخنرانی جداگانه خواهیم داشت، بنابراین اطلاعات کافی در مورد آنها برای اولین آشنایی وجود دارد. همین! امیدواریم درک خوبی از تفاوت‌های بین رابط‌ها و کلاس‌های انتزاعی داشته باشید و آماده پاسخگویی به هر سوالی باشید، حتی یک سوال دشوار :) OutputStreamFileInputStreamFileOutputStreamBufferedInputStream
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION