1. Veri akışları

Nadiren bir program kendi başına bir ada olarak var olur. Programlar genellikle bir şekilde "dış dünya" ile etkileşime girer. Bu, klavyeden veri okuyarak, mesaj göndererek, İnternet'ten sayfa indirerek veya tersine uzak bir sunucuya dosya yükleyerek olabilir.

Tüm bu davranışları tek kelime ile ifade edebiliriz: Program ile dış dünya arasındaki veri alışverişi . Bekle, bu sadece bir kelime değil.

Elbette, veri alışverişinin kendisi iki kısma ayrılabilir: veri alma ve veri gönderme. Örneğin, bir nesneyi kullanarak klavyeden veri okuyorsunuz Scanner- bu veri alıyor. Ve bir komut kullanarak verileri ekranda görüntülersiniz System.out.println()— bu, veri göndermektir.

Programlamada, veri alışverişini tanımlamak için "akım" terimi kullanılır. Bu terim nereden geldi?

Gerçek hayatta, bir su akışına veya bir bilinç akışına sahip olabilirsiniz. Programlamada, veri akışlarımız var .

Akışlar çok yönlü bir araçtır. Programın herhangi bir yerden veri almasına (giriş akışları) ve herhangi bir yere veri göndermesine (çıkış akışları) izin verirler. Böylece, iki tür vardır:

  • Bir giriş akışı veri almak içindir
  • Çıkış akışı veri göndermek içindir

Akışları "somut" hale getirmek için Java'nın yaratıcıları iki sınıf yazdı: InputStreamve OutputStream.

Sınıfın, ondan veri okumanıza izin veren InputStreambir yöntemi vardır . read()Ve sınıfın , ona veri yazmanıza izin veren OutputStreambir yöntemi vardır . write()Başka yöntemleri de var, ama daha sonraları.

Bayt akışları

Ne tür verilerden bahsediyoruz? Hangi formatı alıyor? Başka bir deyişle, bu sınıflar hangi veri türlerini destekliyor?

Bunlar genel sınıflardır, dolayısıyla en yaygın veri türü olan byte. Bir OutputStreambayt (ve bayt dizileri) yazabilir ve bir InputStreamnesne baytları (veya bayt dizilerini) okuyabilir. İşte bu - başka veri türlerini desteklemiyorlar.

Sonuç olarak, bu akışlara bayt akışları da denir .

Akışların bir özelliği, verilerinin yalnızca sıralı olarak okunabilmesi (veya yazılabilmesidir). Önüne gelen tüm verileri okumadan akışın ortasındaki verileri okuyamazsınız.

Sınıf boyunca klavyeden veri okuma şu şekilde çalışır Scanner: klavyeden verileri sırayla, satır satır okursunuz. Bir satırı, ardından bir sonraki satırı, ardından bir sonraki satırı vb. okuruz. Uygun olarak, satırları okuma yöntemi denir nextLine().

Bir'e veri yazmak OutputStreamda sıralı olarak gerçekleşir. Bunun güzel bir örneği konsol çıktısıdır. Bir satırın çıktısını alırsınız, ardından bir başkası ve bir başkası gelir. Bu sıralı çıktıdır. İlk satırın, ardından onuncu ve ardından ikinci satırın çıktısını alamazsınız. Tüm veriler bir çıkış akışına yalnızca sıralı olarak yazılır.

Karakter akışları

Kısa bir süre önce dizelerin en popüler ikinci veri türü olduğunu öğrendiniz ve gerçekten öyleler. Karakterler ve tüm diziler şeklinde pek çok bilgi aktarılır. Bir bilgisayar, her şeyi bayt olarak gönderip almakta mükemmeldir, ancak insanlar o kadar da mükemmel değildir.

Bu gerçeği hesaba katarak, Java programcıları iki sınıf daha yazdı: Readerve Writer. Sınıf , sınıfa Readerbenzer InputStream, ancak yöntemi read()baytları değil karakterleri ( char) okur. Sınıf Writer, sınıfa karşılık gelir OutputStream. Ve tıpkı sınıf gibi , baytlarla değil Readerkarakterlerle ( ) çalışır .char

Bu dört sınıfı karşılaştırırsak, aşağıdaki resmi elde ederiz:

Bayt (bayt) Karakterler (karakter)
veri okuma
InputStream
Reader
veri yazma
OutputStream
Writer

Pratik uygulama

, , ve sınıfların kendileri, InputStreamverilerin okunabileceği (veya verilerin yazılabileceği) herhangi bir somut nesneyle ilişkilendirilmediğinden, doğrudan kimse tarafından kullanılmaz. Ancak bu dört sınıf, çok şey yapabilen çok sayıda alt sınıfa sahiptir.OutputStreamReaderWriter


2. InputStreamsınıf

Sınıf InputStreamilginçtir çünkü yüzlerce alt sınıf için üst sınıftır. Kendine ait herhangi bir verisi yoktur, ancak tüm türetilmiş sınıflarının miras aldığı yöntemleri vardır.

Genel olarak, akış nesnelerinin verileri dahili olarak depolaması nadirdir. Akış, verileri okumak/yazmak için bir araçtır, ancak depolama değildir. Bununla birlikte, istisnalar var.

InputStreamSınıfın ve onun soyundan gelen tüm sınıfların yöntemleri :

Yöntemler Tanım
int read()
Akıştan bir bayt okur
int read(byte[] buffer)
Akıştan bir dizi bayt okur
byte[] readAllBytes()
Akıştan tüm baytları okur
long skip(long n)
nAkıştaki baytları atlar (onları okur ve atar)
int available()
Akışta kaç bayt kaldığını kontrol eder
void close()
Akışı kapatır

Bu yöntemleri kısaca inceleyelim:

read()yöntem

Yöntem , akıştan bir baytread() okur ve onu döndürür. Dönüş türüyle kafanız karışmış olabilir . Bu tür, standart tamsayı türü olduğu için seçildi . öğesinin ilk üç baytı sıfır olacaktır.intintint

read(byte[] buffer)yöntem

Bu, yöntemin ikinci çeşididir read(). InputStreamBir kerede bir bayt dizisini okumanıza izin verir . Baytları saklayacak dizi bir argüman olarak iletilmelidir. Yöntem bir sayı döndürür - gerçekte okunan bayt sayısı.

Diyelim ki 10 kilobaytlık bir arabelleğiniz var ve sınıfı kullanarak bir dosyadan veri okuyorsunuz FileInputStream. Dosya yalnızca 2 kilobayt içeriyorsa, tüm veriler arabellek dizisine yüklenir ve yöntem 2048 (2 kilobayt) sayısını döndürür.

readAllBytes()yöntem

Çok iyi bir yöntem. Yalnızca bitene kadar tüm verileri okur InputStreamve tek bir bayt dizisi olarak döndürür. Bu, küçük dosyaları okumak için çok kullanışlıdır. Büyük dosyalar fiziksel olarak belleğe sığmayabilir ve yöntem bir istisna oluşturur.

skip(long n)yöntem

Bu yöntem, nesneden ilk n baytı atlamanıza izin verir InputStream. Veriler kesinlikle sırayla okunduğundan, bu yöntem yalnızca akıştan ilk n baytı okur ve onları atar.

Gerçekte atlanan bayt sayısını döndürür (baytlar atlanmadan akışın sona ermesi durumunda n).

int available()yöntem

Yöntem, akışta kalan bayt sayısını döndürür.

void close()yöntem

Yöntem close(), veri akışını kapatır ve onunla ilişkili harici kaynakları serbest bırakır. Bir akış kapatıldığında, ondan daha fazla veri okunamaz.

Çok büyük bir dosyayı kopyalayan bir örnek program yazalım. readAllBytes()Tüm dosyayı belleğe okumak için yöntemi kullanamayız . Örnek:

kod Not
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileInputStream input = new FileInputStream(src);
FileOutputStream output = new FileOutputStream(dest))
{
   byte[] buffer = new byte[65536]; // 64Kb
   while (input.available() > 0)
   {
      int real = input.read(buffer);
      output.write(buffer, 0, real);
   }
}



InputStreamdosyadan okumak için dosyaya
OutputStreamyazmak için

İçine verileri okuyacağımız arabellek
Akışta veri olduğu sürece

verileri arabelleğe oku
Verileri arabellekten ikinci akışa yaz

Bu örnekte, iki sınıf kullandık: bir dosyadan veri okumak için FileInputStreambir alt sınıf ve bir dosyaya veri yazmak için bir alt sınıf . İkinci ders hakkında biraz sonra konuşacağız.InputStreamFileOutputStreamOutputStream

Burada bir başka ilginç nokta da değişkendir real. Bir dosyadan son veri bloğu okunduğunda, kolayca 64 KB'den daha az veriye sahip olabilir. Buna göre, tamponun tamamını değil, sadece bir kısmını - ilk realbaytları - çıkarmamız gerekiyor. Bu tam olarak yöntemde olan şeydir write().



3. Readersınıf

Sınıf Reader, sınıfın tam bir analoğudur InputStream. charTek fark baytlarla değil karakterlerle ( ) çalışmasıdır . Tıpkı InputStreamsınıf gibi, Readersınıf da kendi başına hiçbir yerde kullanılmaz: yüzlerce alt sınıf için üst sınıftır ve hepsi için ortak yöntemler tanımlar.

Sınıfın yöntemleri Reader(ve onun soyundan gelen tüm sınıflar):

Yöntemler Tanım
int read()
charAkıştan birini okur
int read(char[] buffer)
charAkıştan bir dizi okur
long skip(long n)
n charsAkışta atlar (onları okur ve atar)
boolean ready()
Akışta kalan bir şey olup olmadığını kontrol eder
void close()
Akışı kapatır

InputStreamYöntemler, küçük farklılıklar olsa da, sınıfın yöntemlerine çok benzer .

int read()yöntem

Bu yöntem charakıştan birini okur ve onu döndürür. Tür charan olarak genişler int, ancak sonucun ilk iki baytı her zaman sıfırdır.

int read(char[] buffer)yöntem

Bu, yöntemin ikinci çeşididir read(). ReaderBir kerede hepsinden bir char dizisini okumanıza izin verir . Karakterleri saklayacak dizi argüman olarak iletilmelidir. Yöntem bir sayı döndürür - gerçekte okunan karakter sayısı.

skip(long n)yöntem

Bu yöntem, nesneden ilk n karakteri atlamanıza izin verir Reader. Sınıfın benzer yöntemiyle tamamen aynı şekilde çalışır InputStream. Gerçekte atlanan karakter sayısını döndürür.

boolean ready()yöntem

trueAkışta okunmamış bayt olup olmadığını döndürür .

void close()yöntem

Yöntem close(), veri akışını kapatır ve onunla ilişkili harici kaynakları serbest bırakır. Bir akış kapatıldığında, ondan daha fazla veri okunamaz.

Karşılaştırma için, bir metin dosyasını kopyalayan bir program yazalım:

kod Not
String src = "c:\\projects\\log.txt";
String dest = "c:\\projects\\copy.txt";

try(FileReader reader = new FileReader(src);
FileWriter writer = new FileWriter(dest))
{
   char[] buffer = new char[65536]; // 128Kb
   while (reader.ready())
   {
      int real = reader.read(buffer);
      writer.write(buffer, 0, real);
   }
}



Readerbir dosyadan okumak
Writeriçin bir dosyaya yazmak için

İçine verileri okuyacağımız arabellek
Akışta veri olduğu sürece verileri

bir arabelleğe oku
Verileri arabellekten ikinci akışa yaz