JavaRush /Java блогу /Random-KY /Абстракттуу класстар менен интерфейстердин айырмасы

Абстракттуу класстар менен интерфейстердин айырмасы

Группада жарыяланган
Салам! Бул лекцияда абстракттуу класстар интерфейстерден кандайча айырмаланат жана жалпы абстракттуу класстар менен мисалдарды карап чыгабыз. Абстракттуу класстар менен интерфейстердин айырмасы - 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(мыскылдуу жай) жана андан мурастайлы 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();
    }

    Биз интерфейстин ичинде жеке өзгөрмөлөрдү да түзө албайбыз . Неге? Анткени жеке модификатор колдонуучудан ишке ашырууну жашыруу үчүн түзүлгөн. Бирок интерфейстин ичинде эч кандай ишке ашыруу жок: ал жерде жашыра турган эч нерсе жок.

    Interface жүрүм-турумду гана сүрөттөйт. Демек, интерфейстин ичинде алгычтарды жана орнотуучуларды ишке ашыра албайбыз. Бул интерфейстин табияты: ал абалга эмес, жүрүм-турумга байланыштуу.

    Java8 ишке ашырууга ээ демейки интерфейс ыкмаларын киргизди. Сиз алар жөнүндө мурунтан эле билесиз, ошондуктан биз аларды кайталабайбыз.

  2. Абстракттуу класс абдан тыгыз байланышта болгон класстарды бириктирет жана бириктирет. Ошол эле учурда, бир эле интерфейсти жалпы эч нерсеси жок класстар ишке ашыра алат.

    Келгиле, канаттуулар менен болгон мисалыбызга кайрылып көрөлү.

    BirdАнын негизинде канаттууларды жаратуу үчүн биздин абстракттуу классыбыз керек. Канаттуулар гана, башка эч ким жок! Албетте, алар ар кандай болот.

    Абстракттуу класстар менен интерфейстердин айырмасы - 2

    Интерфейс менен Flyableбаары башкача. Ал бир гана анын атына ылайыктуу жүрүм-турумун сүрөттөйт - "учуу". "Учуу", "учууга жөндөмдүү" деген аныктама бири-бирине тиешеси жок көптөгөн an objectтерди камтыйт.

    Абстракттуу класстар менен интерфейстердин айырмасы - 3

    Бул 4 субъекти бири-бири менен эч кандай байланышы жок. Эмне дейм, алардын баары жандуу да эмес. Бирок, алардын баары Flyableучууга жөндөмдүү.

    Биз аларды абстракттуу класстын жардамы менен сүрөттөй албайбыз. Алардын жалпы абалы же окшош талаалары жок. Учактын мүнөздөмөлөрү үчүн, балким, "модел", "чыгарылган жылы" жана "жүргүнчүлөрдүн максималдуу саны" деген талаалар керек болот. Карлсон үчүн бүгүн ал жеген бардык таттуулар үчүн талаалар жана ал Улак менен ойной турган оюндардын тизмеси бар. Бир чиркей үчүн ... у-ух ... биз да билбейбиз ... Балким, "таарыныч деңгээли"? :)

    Эң негизгиси, биз аларды абстракттуу класс аркылуу сүрөттөй албайбыз. Алар өтө эле башкача. Бирок жалпы жүрүм-турум бар: алар уча алышат. Интерфейс дүйнөдөгү уча, сүзө, секире же башка жүрүм-турумга ээ болгон нерселердин баарын сүрөттөө үчүн идеалдуу.

  3. Класстар каалагандай көп интерфейсти ишке ашыра алышат, бирок алар бир класстан гана мурастай алышат.

    Бул тууралуу биз буга чейин бир нече жолу айтканбыз. Javaда бир нече мурас жок, бирок бир нече ишке ашыруу бар. Бул пункт жарым-жартылай мурункусунан келип чыгат: интерфейс көбүнчө жалпы эч нерсеси жок көптөгөн түрдүү класстарды бириктирет жана бири-бирине абдан жакын класстар тобу үчүн абстракттуу класс түзүлөт. Ошондуктан, сиз бир гана классты мурастай аласыз. Абстракттуу класс "бул" мамилени сүрөттөйт.

Стандарттык InputStream & OutputStream интерфейстери

Биз буга чейин агымдык киргизүү жана чыгаруу үчүн жооптуу ар кандай класстарды басып өттүк. карап көрөлү InputStreamжана OutputStream. Жалпысынан алганда, бул интерфейстер эмес, чыныгы абстракттуу класстар. Эми сиз алардын эмне экенин билесиз, андыктан алар менен иштөө бир топ жеңилдейт :) InputStream- бул byte киргизүү үчүн жооптуу абстрактуу класс. Java-дан мураска алган бир катар класстарга ээ InputStream. Алардын ар бири ар кандай булактардан маалыматтарды алуу үчүн конфигурацияланган. Бул ата-эне болгондуктан InputStream, маалымат агымдары менен ыңгайлуу иштөө үчүн бир нече ыкмаларды камсыз кылат. Ар бир баланын бул ыкмалары бар InputStream:
  • int available()окуу үчүн жеткorктүү byteтардын санын кайтарат;
  • close()киргизүү булагын жабат;
  • int read()агымдагы кийинки жеткorктүү byteтын бүтүн сандык көрүнүшүн кайтарат. Эгерде агымдын аягына жетсе, -1 саны кайтарылат;
  • int read(byte[] buffer)окулган byteтардын санын кайтарып, буферге byteтарды окууга аракет кылат. Файлдын аягына жеткенде -1 кайтарат;
  • int read(byte[] buffer, int byteOffset, int byteCount)byte блогунун бир бөлүгүн окуйт. Маалымат блогу толук толтурулбай калган учурда колдонулат. Ал файлдын аягына жеткенде, -1 кайтарат;
  • long skip(long byteCount)skips byteCount, киргизүү byte, этибарга алынбаган byteтардын санын кайтарат.
Мен сизге методдордун толук тизмесин изилдөөнү сунуштайм . Иш жүзүндө ондон ашык улануучу класстар бар. Бул жерде бир нече мисал келтирилген:
  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 was read" + c);
           }
       } catch(Exception e) {

           e.printStackTrace();

       } finally {

           inputStream.close();
           buffer.close();
       }
   }
}
Бул мисалда биз "D:/Users/UserName/someFile.txt" дареги боюнча компьютерде жайгашкан файлдан маалыматтарды окуп жатабыз . Биз 2 an objectти түзөбүз - FileInputStreamжана BufferedInputStreamанын "орогу" катары. Андан кийин биз файлдан byteтарды окуп, аларды символдорго айлантабыз. Файл аяктаганга чейин. Көрүнүп тургандай, бул жерде татаал эч нерсе жок. Сиз бул codeду көчүрүп, аны компьютериңизде сакталган чыныгы файлда иштетсеңиз болот :) Класс OutputStream- бул byte агымынын чыгышын аныктаган абстракттуу класс. Сиз буга чейин эле түшүнгөндөй, бул "а" антиподу InputStream. Ал маалыматты кайдан окуу үчүн эмес, аны кайда жөнөтүү үчүн жооп берет . Окшош InputStream, бул абстракттуу класс бардык урпактарга ыңгайлуу иштөө үчүн бир топ ыкмаларды берет:
  • int close()чыгаруу агымын жабат;
  • void flush()бардык чыгуу буферлерин тазалайт;
  • abstract void write (int oneByte)чыгаруу агымына 1 byte жазат;
  • void write (byte[] buffer)чыгаруу агымына byte массивдерин жазат;
  • void write (byte[] buffer, int offset, int count)позициянын жылышынан баштап массивден сан byteтарынын диапазонун жазат.
Бул жерде класстын урпактарынын кээ бирлери OutputStream:
  1. DataOutputStream. Стандарттык Java маалымат түрлөрүн жазуу ыкмаларын камтыган чыгаруу агымы.

    Примитивдүү Java түрлөрүн жана саптарын жазуу үчүн абдан жөнөкөй класс. Албетте, сиз жазылган codeду түшүндүрбөстөн да түшүнөсүз:

    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Татаал эч нерсе жок, маңызы (же 'a) менен бирдей 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();
       }
    }

    Дагы бир жолу, сиз бул code менен өзүңүз "ойносоңуз" жана ал сиздин компьютериңиздеги чыныгы файлдарда кандай иштээрин текшере аласыз.

Ошондой эле мураскорлор жөнүндө материалдан окуй аласыз “ InputStreamКиргизүү / Чыгуу системасы ”. О , бизде да өзүнчө лекция болот, ошондуктан алар тууралуу биринчи таанышууга жетиштүү маалымат бар. Баары болду! Сиз интерфейстер менен абстракттуу класстардын ортосундагы айырмачылыктарды жакшы түшүндүңүз жана бардык суроого, атүгүл татаал суроого жооп берүүгө даярсыз деп ишенебиз :) OutputStreamFileInputStreamFileOutputStreamBufferedInputStream
Комментарийлер
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION