ByteArrayOutputStream

All lectures for UZ purposes
Daraja, Dars
Mavjud

ByteArrayOutputStream sinfi chiqish oqimini amalga oshiradi, bunda ma'lumotlar baytli massivga yoziladi. Bafer avtomatik ravishda o'sib boradi, chunki unga ma'lumotlar yoziladi.

ByteArrayOutputStream sinfi oqimi xotirada bafer yaratadi va oqimga yuborilgan barcha ma'lumotlar baferda saqlanadi.

ByteArrayOutputStream Konstruktorlari

ByteArrayOutputStream sinfida quyidagi konstruktorlar mavjud:

Konstruktor
ByteArrayOutputStream() Konstruktor 32 baytli xotirada bafer yaratadi.
ByteArrayOutputStream(int a) Konstruktor xotirada aniqlangan o'lcham bilan bafer yaratadi.

Sinf ichida qanday ko'rinishga ega:


//ma'lumotlar saqlanadigan baferning o'zi.
protected byte buf[];

//baferga yozilgan joriy baytlar soni.
protected int count;

public ByteArrayOutputStream() {
    this(32);
}

public ByteArrayOutputStream(int size) {
    if (size < 0) {
        throw new IllegalArgumentException("Negative initial size: "
                                           + size);
    }
    buf = new byte[size];
}
    

ByteArrayOutputStream Metodlari

Keling, sinfimizda foydalanishimiz mumkin bo'lgan metodlar haqida gaplashamiz.

Oqimimizga biror narsa yozaylik. Buning uchun write() metodidan foydalanamiz — u bitta bayt yoki baytlar to'plamini qabul qilib yozishi mumkin.

Metod
void write(int b) Bitta bayt yozish.
void write(byte b[], int off, int len) Ma'lum o'lchamdagi bayt massivi yozish.
void writeBytes(byte b[]) Bayt massivini yozish.
void writeTo(OutputStream out) Joriy chiqish oqimidagi barcha ma'lumotlarni ko'rsatilgan chiqish oqimiga yozadi.

Metodlarning amalga oshirilishi:


public static void main(String[] args) throws IOException {
   ByteArrayOutputStream outputByte = new ByteArrayOutputStream();
   //bitta bayt yozish
   while(outputByte.size()!= 8) {
      outputByte.write("javarush".getBytes());
   }

   //bayt massivini yozish
   String value = "\nWelcome to Java\n";
   byte[] arrBytes = value.getBytes();
   outputByte.write(arrBytes);

   //o'lcham bo'yicha yozish
   String javaRush = "JavaRush";
   byte[] b = javaRush.getBytes();
   outputByte.write(b, 4, 4);

   //faylga yozish
   FileOutputStream fileOutputStream = new FileOutputStream("output.txt");
   outputByte.write(80);
   outputByte.writeTo(fileOutputStream);
}
    

Natijada bizda output.txt fayli ichidagi matn bilan yaratiladi:

toByteArray() metodi joriy chiqish oqimidagi mazmunni baytlar massivi sifatida qaytaradi. toString() metodidan foydalanib, buf baytlar massivini matn ko'rinishida olishingiz mumkin:


public static void main(String[] args) throws IOException {
    ByteArrayOutputStream outputByte = new ByteArrayOutputStream();

    String value = "JavaRush";
    outputByte.write(value.getBytes());

    byte[] result = outputByte.toByteArray();
    System.out.println("Mazmuni chiqarish:");

    for(int i = 0 ; i < result.length; i++) {
        // Belgilarni chiqarish
        System.out.print((char)result[i]);
    }
}
    

Natijada — baferimizda biz unga bergan baytlar massivi mavjud bo'ladi.

reset() metodi baytli massivning chiqish oqimidagi amaldagi baytlar sonini nolga qaytaradi (shuning uchun chiqishda yig'ilganlar chiqariladi).


public static void main(String[] args) throws IOException {
   ByteArrayOutputStream outputByte = new ByteArrayOutputStream(120);

   String value = "JavaRush";
   outputByte.write(value.getBytes());
   byte[] result = outputByte.toByteArray();
   System.out.println("Resetgacha chiqarish: ");

   for (byte b : result) {
      // Belgilarni chiqarish
      System.out.print((char) b);
   }

   outputByte.reset();

   byte[] resultAfterReset = outputByte.toByteArray();
   System.out.println("\nResetdan keyin mazmuni chiqarish:");

   for (byte b : resultAfterReset) {
      // Belgilarni chiqarish
      System.out.print((char) b);
   }
}
    

reset() metodidan so'ng baferimizni chiqarishda hech narsa olinmaydi.

close() metodining xususiyatlari

Bu metod alohida e'tiborga loyiq. Uning nima qilayotganini tushunish uchun ichiga kiraylik:


/**
 * Closing a {@code ByteArrayOutputStream} has no effect. The methods in
 * this class can be called after the stream has been closed without
 * generating an {@code IOException}.
 */
public void close() throws IOException {
}
    

close() metodi ByteArrayOutputStreamda, aslida, hech narsa qilmaydi.

Nega shunday? ByteArrayOutputStream — bu xotira asosidagi oqim (ya'ni, foydalanuvchi tomonidan kodda boshqariladigan va to'ldiriladigan), shuning uchun close() chaqirilganda hech qanday ta'sir o'rnatilmaydi.

Amaliyot

Endi ByteArrayOutputStream va ByteArrayInputStreamdan foydalanib fayl tizimini amalga oshirib ko'ramiz.

FileSystem sinfini Singleton andozasidan foydalanib yozamiz va statik HashMap<String, byte[]>dan foydalanamiz, bu yerda:

  • String — faylga yo'l;
  • byte[] — saqlangan fayldagi ma'lumotlar.

import java.io.*;
import java.util.HashMap;
import java.util.Map;

class FileSystem {
   private static final FileSystem fileSystem = new FileSystem();
   private static final Map<String, byte[]> files = new HashMap<>();

   private FileSystem() {
   }

   public static FileSystem getFileSystem() {
       return fileSystem;
   }

   public void create(String path) {
       validateNotExists(path);
       files.put(path, new byte[0]);
   }

   public void delete(String path) {
       validateExists(path);
       files.remove(path);
   }

   public boolean isExists(String path) {
       return files.containsKey(path);
   }

   public InputStream newInputStream(String path) {
       validateExists(path);
       return new ByteArrayInputStream(files.get(path));
   }

   public OutputStream newOutputStream(String path) {
       validateExists(path);
       return new ByteArrayOutputStream() {
           @Override
           public void flush() throws IOException {
               final byte[] bytes = toByteArray();
               files.put(path, bytes);
               super.flush();
           }

           @Override
           public void close() throws IOException {
               final byte[] bytes = toByteArray();
               files.put(path, bytes);
               super.close();
           }
       };
   }

   private void validateExists(String path) {
       if (!files.containsKey(path)) {
           throw new RuntimeException("File not found");
       }
   }

   private void validateNotExists(String path) {
       if (files.containsKey(path)) {
           throw new RuntimeException("File exists");
       }
   }
}
    

Bu sinfda biz umumiy metodlar yaratdik:

  • standart CRUD (yaratish, o'qish, yangilash, o'chirish) metodlari,
  • fayl mavjudligini tekshirish metodi,
  • fayl tizimi instansini olish metodi.

Fayldan o'qish uchun InputStreamni qaytaramiz. Ichkarida — ByteArrayInputStreamning amalga oshirilishi. Bafer files xaritasi ichida saqlangan baytli massivdir.

Ikkinchi qiziqarli metod — newOutputStream. Ushbu metod chaqirilganda, foydalanuvchiga ByteArrayOutputStream tipida yangi ob'ekt qaytaramiz, ikkita o'zgaritilgan metodlar bilan: flush va close. Ushbu metodlardan har biri chaqirilganda yozish kerak bo'lgan trigerdir.

Aniq qilib aytsak: foydalanuvchi nimani yozishi mumkin bo'lgan baytli massivni olamiz va uni mos qiymat sifatida files xaritasiga saqlaymiz.

Yozilgan fayl tizimini (FS) tekshirish uchun quyidagi koddan foydalanamiz:


import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import static java.nio.charset.StandardCharsets.UTF_8;

public class MyFileSystemTest {
   public static void main(String[] args) throws IOException {
       FileSystem fileSystem = FileSystem.getFileSystem();
       final String path = "/user/bin/data.txt";

       // Fayl yaratamiz
       fileSystem.create(path);
       System.out.println("Fayl muvaffaqiyatli yaratildi");

       // Tekshiramiz, u bo'shligini
       try (InputStream inputStream = fileSystem.newInputStream(path)) {
           System.out.print("Fayl mazmuni:\t");
           System.out.println(read(inputStream));
       }

       // Unda ma'lumot yozamiz
       try (final OutputStream outputStream = fileSystem.newOutputStream(path)) {
           outputStream.write("JavaRush".getBytes(UTF_8));
           System.out.println("Ma'lumot faylga yozildi");
       }

       // Ma'lumotlarni o'qimiz
       try (InputStream inputStream = fileSystem.newInputStream(path)) {
           System.out.print("Fayl mazmuni:\t");
           System.out.println(read(inputStream));
       }

       // Faylni o'chiramiz
       fileSystem.delete(path);

       // FSda bunday fayl mavjud emasligini tekshiramiz
       System.out.print("Fayl mavjud:\t");
       System.out.println(fileSystem.isExists(path));

   }

   private static String read(InputStream inputStream) throws IOException {
       return new String(inputStream.readAllBytes(), UTF_8);
   }
}
    

Test qilishda quyidagi amallarni amalga oshiramiz:

  1. Yangi fayl yaratamiz.
  2. Yaratilgan fayl bo'shligini tekshiramiz.
  3. Faylga ma'lumot kiritamiz.
  4. Yozilgan ma'lumotlarni o'qib, ularning to'g'ri ekanligiga ishonch hosil qilamiz.
  5. Faylni o'chiramiz.
  6. Fayl o'chirilganligini tekshiramiz.

Ushbu kod ishlatilganda quyidagi natija olinadi:

Fayl muvaffaqiyatli yaratildi
Fayl mazmuni:
Ma'lumot faylga yozildi Fayl mazmuni: JavaRush
Fayl mavjud: false

Bu misol nega kerak edi?

Hamma narsalar oddiy: har qanday ma'lumot — bu baytlar to'plami. Agar siz diskdan ko'p va tez-tez o'qishingiz va yozishingiz kerak bo'lsa, kod IO muammosi tufayli sekin ishlaydi. Bunday holda, RAMda virtual fayl tizimini saqlash mantiqli bo'lib, undan oddiy disklar kabi foydalanish mumkin. Va InputStream va OutputStreamdan osonroq nima bo'lishi mumkin?

Albatta, bu o'quv misoli, ishlab chiqarishga tayyor kod emas. Bunda quyidagilar hisobga olinmagan (to'liq ro'yxat emas):

  • ko'p jarayonli kirish;
  • fayllar hajmi (JVM uchun mavjud RAM hajmi);
  • yo'llarning strukturasini tekshirish yo'q;
  • parametrlar sifatida berilgan ma'lumotlarni tekshirish yo'q.

Qiziqarli ma'lumot:
JavaRush vazifalarini tasdiqlash serverida shunga o'xshash yondashuv ishlatiladi. Virtual FS ishga tushiriladi, aynan qaysi testlarni topshiriqni tekshirish uchun ishga tushirish kerakligini aniqlab olinadi, va natijalarni o'qish va test qilish amalga oshiriladi.

Natija va asosiy savol

Leksiyani o'qib bo'lgach, eng asosiy savol: "Nega men shunchaki byte[]dan foydalana olmayman, axir bu qulayroq va cheklangan emasmi?"

ByteArrayInputStreamning afzalligi — u siz faqat o'qish uchun baytlardan foydalanmoqchisiz degan juda ishonarli tasdiq vazifasini bajaradi (chunki oqim ularni o'zgartirish uchun interfeys bermaydi). Har holda, dasturchi baytlarga bevosita murojaat qilishi mumkin bo'lsa ham.

Agar bu ba'zida byte[], ba'zida fayl, ba'zida esa tarmoq ulanishi va hokazo bo'lsa, sizda "bayt oqimi, va ularning manbai muhim emas" uchun biror qanday abstraksiya kerak bo'ladi. Mana shu InputStream nima ekanligini anglatadi. Manba baytlar massivi bo'lib chiqsa, ByteArrayInputStream yaxshi foydalanish uchun InputStream bo'ladi.

Bu ko'plab holatlarda foydali, lekin ikkita aniq misol keltiraylik:

  1. Siz kutubxona yozasiz, u baytlarni qabul qiladi va ularni qandaydir tarzda ishlaydi (masalan, bu tasvirlarni qayta ishlash kutubxonasi). Kutubxonangiz foydalanuvchilari fayldan, xotiradagi byte[]dan yoki boshqa manbadan baytlarni berishi mumkin. Shunday qilib, siz InputStream qabul qiluvchi interfeysni taqdim etasiz — bu byte[]lari bo'lsa, ular uni ByteArrayInputStream ichiga o'rab qo'yishlari kerak bo'ladi.

  2. Siz tarmoq ulanishini o'qiydigan kod yozasiz. Ammo ushbu kodni bir birlik tekshiruvi uchun siz ulanishni ochishingiz kerak emas; faqat ba'zi baytlarni kodga qo'shmoqchisiz. Shunday qilib, kod InputStream qabul qiladi va sizning testi ByteArrayInputStreamni taqdim etadi.

Izohlar
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION