CodeGym /وبلاگ جاوا /Random-FA /تفاوت بین کلاس های انتزاعی و رابط ها
John Squirrels
مرحله
San Francisco

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

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

    به عنوان مثال، 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 روش های پیش فرضی را برای رابط هایی که پیاده سازی دارند معرفی کرد. شما قبلاً در مورد آنها می دانید، بنابراین ما خودمان را تکرار نمی کنیم.

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

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

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

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

    با CanFlyرابط، هر کس به روش خود کار می کند. این فقط رفتار (پرواز) مرتبط با نام آن را توصیف می کند. بسیاری از چیزهای نامرتبط "می توانند پرواز کنند".

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

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

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

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

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

    ما قبلاً بیش از یک بار به این موضوع اشاره کرده ایم. جاوا وراثت چندگانه کلاس ها را ندارد، اما از وراثت چندگانه رابط ها پشتیبانی می کند. این نکته تا حدی از مورد قبلی پیروی می کند: یک رابط کلاس های مختلف زیادی را که اغلب هیچ چیز مشترک دیگری ندارند، به هم متصل می کند، در حالی که یک کلاس انتزاعی برای گروهی از کلاس های بسیار نزدیک ایجاد می شود. بنابراین، منطقی است که شما فقط می توانید یک کلاس را به ارث ببرید. یک کلاس انتزاعی رابطه "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 بایت ها را در جریان ورودی رد می کند و تعداد بایت های نادیده گرفته شده را برمی گرداند.
توصیه می کنم لیست کامل روش ها را مطالعه کنید . در واقع بیش از ده کلاس کودک وجود دارد. به عنوان مثال، در اینجا چند مورد وجود دارد:
  1. FileInputStream: رایج ترین نوع InputStream. برای خواندن اطلاعات از یک فایل استفاده می شود.
  2. StringBufferInputStream: نوع مفید دیگری از InputStream. یک رشته را به یک تبدیل می کند 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 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کلاس آورده شده است:
  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'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، بنابراین این اطلاعات برای اولین آشنایی کافی است. خودشه! امیدواریم تفاوت بین رابط ها و کلاس های انتزاعی را درک کرده باشید و آماده پاسخگویی به هر سوالی، حتی سوالات ترفندی باشید :)
نظرات
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION