Bölüm 7: Giriş Çıkış

Java ile Nesne Yönelimli Programlama
AkışlarMetin ve ikili dosyalarKarakter kodlamaFile ve Scanner Reader / WriterTamponlamaDosya kopyalamaRasgele erişim SerileştirmeNIO: tampon, kanal, seçici
Sercan KÜLCÜ · Giresun Üniversitesi · Elektrik-Elektronik Mühendisliği

Giriş

Öğrenme Çıktıları

Akışı açıklamak

Girdi/çıktı akışını ve System.in, System.out, System.err akışlarını anlatmak; klavyeden Scanner ile okumak.

Metin ile ikiliyi ayırmak

Metin ve ikili dosya farkını, ASCII, ISO-8859-9 ve UTF-8 kodlamalarını, bir sayının dosyada kaç byte tuttuğunu hesaplamak.

Dosya okumak ve yazmak

File, Scanner, BufferedReader, PrintWriter, FileWriter ile metin dosyası işlemek; kaynakları try-with-resources ile kapatmak.

Doğru akışı seçmek

Bayt (InputStream/OutputStream) ve karakter (Reader/Writer) akışlarını, tamponlu ve sarmalayıcı akışları ayırt etmek.

Kopyalamak, konumlanmak

Bayt, blok ve satır düzeyinde kopyalamak; RandomAccessFile ile istenen konuma atlamak.

Nesne saklamak ve NIO

Nesneleri serileştirmek (Serializable, transient); NIO tamponu, kanalı ve seçicisinin ne işe yaradığını açıklamak.

Akışlar

Akış (Stream)

  • Akış (stream), veriyi bir kaynaktan bir hedefe sırayla, parça parça taşıyan yapıdır. Veri karakter, sayı veya ham byte olabilir.
  • Girdi akışı (input stream): verinin programa girdiği akış. Örnek: System.in (klavye).
  • Çıktı akışı (output stream): verinin programdan çıktığı akış. Örnek: System.out (ekran).
  • Benzetme: su borusu. Bir uçtan giren su öbür uçtan aynı sırayla çıkar; borunun öbür ucunda musluk mu depo mu olduğu sizi ilgilendirmez.
KlavyeDosyaAğ (soket)Bellek (dizi)0110 1001 …Girdi akışıInputStream / ReaderJavaprogramı… 0100 1010Çıktı akışıOutputStream / WriterEkranDosyaAğ (soket)Bellek (dizi)

Kaynak ister klavye, ister dosya, ister ağ olsun, program aynı read() / write() metotlarını kullanır. Java G/Ç (giriş/çıkış, I/O) kütüphanesinin gücü bu ortak arayüzdedir.

Akışlar

Klavye ve Ekran: Standart Akışlar

  • Geçici veri işleme: klavye ve ekran verisi saklanmaz; program kapanınca kaybolur.
  • Anında akış: kullanıcı yazdıkça girdi gelir, program yazdıkça ekranda görünür.
  • Her Java programı açılırken üç hazır akış alır:
AkışJava nesnesiTürü
standart girdiSystem.inInputStream
standart çıktıSystem.outPrintStream
standart hataSystem.errPrintStream
Java programı(süreç)Konsol (metin terminali)KlavyeEkran#0 System.in#1 System.out#2 System.err
System.err da ekrana yazar ama ayrı bir kanaldır: çıktı bir dosyaya yönlendirilse bile (java Uygulama > sonuc.txt) hata mesajları ekranda kalır.

Akışlar

İlk Örnek: Okuma ve Yazma

import java.util.Scanner;

public class AkisOrnek {
  public static void main(String[] args) {
    // Giriş akışından veri okuma
    Scanner scanner = new Scanner(System.in);
    int kullaniciGirdisi = scanner.nextInt();
    // Çıkış akışına veri yazma
    System.out.println("Merhaba, Dünya!");
    System.out.println("Girdiğiniz sayı: " + kullaniciGirdisi);
    System.err.println("Bu satır hata akışına yazıldı.");
  }
}
Konsol (girdi: 42)
42
Merhaba, Dünya!
Girdiğiniz sayı: 42
Bu satır hata akışına yazıldı.
  • new Scanner(System.in): klavye akışını okuyan bir tarayıcı oluşturur. Scanner, java.util paketindedir; import şarttır.
  • nextInt() kullanıcı bir sayı yazıp Enter'a basana kadar bekler (bloklanır).
  • System.out.println çıktı akışına bir satır yazar.
  • System.err.println hata akışına yazar; çoğu IDE bu satırı kırmızı gösterir.
  • Kullanıcı sayı yerine abc yazarsa InputMismatchException fırlatılır.

Akışlar

Scanner ile Klavyeden Okuma: Adım Adım

System.in tamponusıradaki

Scanner nesnesi System.in akışına bağlanır. Klavye tamponu boş.

System.in tamponusıradaki

print (println değil) soruyu yazar; kullanıcı cevabı aynı satıra yazar.

System.in tamponuAyşe Yılmaz⏎sıradaki

Kullanıcı Ayşe Yılmaz yazıp Enter'a basar. nextLine() boşluk dâhil bütün satırı alır, satır sonunu (⏎) atar.

System.in tamponuAyşe Yılmaz⏎sıradaki

İkinci soru yazılır.

System.in tamponuAyşe Yılmaz⏎19⏎sıradaki

nextInt() rakamları okuyup int 19'a çevirir. Dikkat: satır sonu (⏎) tamponda kalır.

System.in tamponuAyşe Yılmaz⏎19⏎sıradaki

Okunan değer artık sıradan bir int: 19 + 1 = 20.

System.in tamponuAyşe Yılmaz⏎19⏎sıradaki

Çıktı akışına satırlar yazılır.

System.in tamponuAyşe Yılmaz⏎19⏎sıradaki

String birleştirme: "Seneye " + 20 + " yaşındasın.".

System.in tamponuAyşe Yılmaz⏎19⏎sıradaki

close() tarayıcıyla birlikte System.in'i de kapatır; program içinde bundan sonra klavyeden okunamaz.

import java.util.Scanner;

public class KonsolOkuma {
  public static void main(String[] args) {
    Scanner klavye = new Scanner(System.in);
    System.out.print("Adınız: ");
    String ad = klavye.nextLine();
    System.out.print("Yaşınız: ");
    int yas = klavye.nextInt();
    int seneye = yas + 1;
    System.out.println("Merhaba " + ad + "!");
    System.out.println("Seneye " + seneye + " yaşındasın.");
    klavye.close();
  }
}

Dosyalar ve Kodlama

Dosyalar

  • Dosya, veriyi diskte kalıcı olarak saklar: program kapansa, bilgisayar yeniden başlasa da veri durur (RAM'deki değişkenler ise kaybolur).
  • Her dosyadaki veri sonuçta 0'lar ve 1'ler, yani byte dizisidir. Farkı, bu byte'ların nasıl yorumlandığı yaratır.
  • Metin dosyaları (text): byte'lar bir karakter kodlamasına göre harflere çevrilir; insanlar okuyabilir.
  • İkili dosyalar (binary): byte'lar sayı, piksel, ses örneği, makine komutu gibi verilerdir; ancak onları bilen bir program anlamlandırır.

dosya.txt bir onaltılık (hex) düzenleyicide:

0000  4D 65 72 68 61 62 61 2C  Merhaba,
0008  20 64 C3 BC 6E 79 61 21   d..nya!

Solda konum, ortada her byte'ın onaltılık değeri, sağda ASCII karşılığı. 4D = 77 = M. 15 harf 16 byte tutar: ü UTF-8'de iki byte'tır (C3 BC).

Dosyalar ve Kodlama

Dosya İşleme Süreçleri

OluşturcreateNewFile()Açnew FileReader(…)Yaz / Okuwrite() / read()Kapatclose()tekrar
AdımNe olur?Java'da
OluşturmaDiskte yeni, boş bir dosya oluşturulur.file.createNewFile(); yazmak için açmak da oluşturur
AçmaVar olan dosyaya bir akış bağlanır.new FileInputStream(ad), new FileReader(ad), new Scanner(file)
YazmaVeri dosyaya aktarılır.write(), print(), println()
OkumaVeri dosyadan programa alınır.read(), readLine(), nextLine()
KapatmaTamponda kalan veri diske yazılır, işletim sistemi kaynağı bırakılır.close() veya try-with-resources

Kapatılmayan dosya hem veri kaybına (tamponda bekleyen byte'lar yazılmaz) hem de kaynak sızıntısına yol açar: işletim sisteminin aynı anda açık tutabileceği dosya sayısı sınırlıdır.

Dosyalar ve Kodlama

İlk Dosya Programı

import java.io.*;

public class IlkDosya {
  public static void main(String[] args) throws IOException {
    // Metin dosyasına yazma
    FileWriter writer = new FileWriter("dosya.txt");
    writer.write("Merhaba, dünya!");
    writer.close();
    // İkili dosyaya bir byte yazma
    FileOutputStream cikis = new FileOutputStream("veri.dat");
    cikis.write(127);
    cikis.close();
    // İkili dosyadan okuma
    FileInputStream stream = new FileInputStream("veri.dat");
    int veri = stream.read();
    stream.close();
    System.out.println("Okunan byte: " + veri);
    File metin = new File("dosya.txt");
    System.out.println("dosya.txt: " + metin.length() + " byte");
  }
}
Çıktı
Okunan byte: 127
dosya.txt: 16 byte
dosya.txt
Merhaba, dünya!
  • FileWriter: metin yazar.
  • FileOutputStream: ham byte yazar; write(127) tek byte 7F.
  • read(): byte'ı 0–255 arası int döndürür.

Dosyalar ve Kodlama

Metin Dosyaları

  • Metin dosyaları yazdırılabilir karakterler (harf, rakam, noktalama, boşluk, satır sonu) içerir.
  • Not Defteri, VS Code gibi metin düzenleyicileriyle açılıp düzenlenebilir. Örnek: kaynak kodlar (.java, .c), .txt, .csv, .html.
  • Karakter kümesi / kodlama (character set / encoding): her karakterin hangi sayıyla (byte'larla) saklanacağını belirleyen tablodur. Yazan ve okuyan aynı kodlamayı kullanmalıdır.
ASCII

7 bit, 128 karakter: İngilizce harfler, rakamlar, noktalama, kontrol karakterleri.

ISO-8859-1

8 bit, 256 karakter: Batı Avrupa (Latin-1). é, ü, ö, ç var; ş, ğ, ı yok.

ISO-8859-9

8 bit, Latin-5: Türkçe sürüm. Latin-1'deki 6 harfin yerine ğ, Ğ, ı, İ, ş, Ş gelir.

UTF-8

Unicode'un evrensel kodlaması: tüm dünya dilleri, 1–4 byte. Web'in ve Java 18+ sürümünün varsayılanı.

Dosyalar ve Kodlama

İkili Dosyalar

  • Byte'ları karakter olarak değil, programın belirlediği biçimde (format) saklar: sayılar, pikseller, ses örnekleri, makine kodu.
  • Bilgisayar (doğru program) için anlamlıdır; insanlar doğrudan okuyamaz.
  • Örnekler: çalıştırılabilir dosyalar (.exe, Java'nın .class dosyaları), görüntü (.png, .jpg), müzik (.mp3), video (.mp4), veri dosyaları (.dat).
  • Avantajı: daha küçük ve daha hızlı işlenir; sayıyı metne çevirmek gerekmez.

IlkDosya.class hex düzenleyicide (ilk 32 byte):

0000  CA FE BA BE 00 00 00 41  .......A
0008  00 5C 0A 00 02 00 03 07  .\......
0010  00 04 0C 00 05 00 06 01  ........
0018  00 10 6A 61 76 61 2F 6C  ..java/l

Aynı dosya Not Defteri ile açılınca:

Êþº¾···A·\················java/lang/Object···<init>···()V······java/io/FileWriter······dosya.txt·············(

Her .class dosyası CA FE BA BE byte'larıyla başlar: JVM dosyanın türünü bu “sihirli sayıdan” tanır.

Dosyalar ve Kodlama

ASCII Tablosu

  • ASCII (American Standard Code for Information Interchange): bilgisayarlar arasında metin değişimini standartlaştırmak için 1963'te tanımlanan karakter kodlamasıdır.
  • 7 bit kullanır: 27 = 128 karakter (0–127). Her karakter bir sayı ile temsil edilir.
  • Temel karakterler: harf, rakam, noktalama, temel semboller (32–126). Kontrol karakterleri: iletimde özel işlevli, ekrana basılmayan karakterler (0–31 ve 127): satır sonu, sekme, zil…
032486597127kontrol (0–31)rakamlarbüyük harfküçük harf127 DEL
KarakterAa0boşluk\n (LF)\t sekme
Onluk65974832109
Onaltılık416130200A09
İkilik010000010110000100110000001000000000101000001001

Büyük ve küçük harf arasındaki fark her zaman 32'dir (A = 65, a = 97): tek bir bitin farkı.

Dosyalar ve Kodlama

Java'da char ve Kod Değeri

public class AsciiOrnek {
  public static void main(String[] args) {
    char harf = 'A';
    int kod = harf;                       // char -> int: 65
    System.out.println(harf + " = " + kod);
    System.out.println((int) 'a');        // 97
    System.out.println((char) 97);        // a
    System.out.println((char) (harf + 2)); // C
    System.out.println('7' - '0');        // 7 (rakamın değeri)
    System.out.println(Integer.toBinaryString('A'));
  }
}
Çıktı
A = 65
97
a
C
7
1000001
  • char 16 bitlik bir sayıdır; ilk 128 değeri ASCII ile aynı.
  • int → char için (char) dönüşümü gerekir.
  • '7' - '0' = 55 − 48 = 7.

Dosyalar ve Kodlama

Genişletilmiş ASCII ve Türkçe Karakterler

  • Genişletilmiş ASCII (extended ASCII): 8 bit (1 byte) kullanarak 28 = 256 karakter temsil eder.
  • 0–127: standart ASCII karakterleri, her tabloda aynıdır. 128–255: özel karakterler, semboller ve ulusal harfler.
  • Tek bir “genişletilmiş ASCII” yoktur: üst yarı kod sayfasına (code page) göre değişir. Türkçe için ISO-8859-9 (Latin-5) ve Windows-1254 kullanılır.
ByteD0DDDEF0FDFEFCE7
ISO-8859-1 (Batı Avrupa)ÐÝÞðýþüç
ISO-8859-9 (Türkçe)ĞİŞğışüç
Aynı byte (FE) bir tabloda ş, ötekinde þ'dir. Dosyayı yanlış kod sayfasıyla açmak “þimdi” gibi bozuk metinler üretir. Çözüm: tüm dilleri kapsayan Unicode / UTF-8.

Dosyalar ve Kodlama

Unicode ve UTF-8

  • Unicode, dünyadaki her karaktere tek bir numara (kod noktası, code point) verir: A = 65, ş = 351 (U+015F), € = 8364.
  • UTF-8 bu numarayı 1–4 byte ile yazar: ASCII karakterleri yine 1 byte (geriye uyumlu), Türkçe harfler 2 byte, € 3 byte. UTF-16 çoğu karakter için 2 byte kullanır; Java'nın char türü UTF-16 birimidir.
KarakterKod noktasıASCIIISO-8859-9UTF-8UTF-16
A6541414100 41
75537373700 37
ş3513FFEC5 9F01 5F
ğ2873FF0C4 9F01 1F
ı3053FFDC4 B101 31
€83643F3FE2 82 AC20 AC

3F = ?: kodlamada karşılığı olmayan karakter soru işaretine dönüşür (veri kaybı). Bu tablo "ş".getBytes(StandardCharsets.UTF_8) gibi çağrılarla Java'da üretildi.

Dosyalar ve Kodlama

Deneyin: Karakter Kodlama

KarakterKod noktasıASCIIISO-8859-9UTF-8UTF-16
Toplam
En fazla 12 karakter yazın; her karakterin byte'ları anında hesaplanır.

Kırmızı 3F: o kodlamada karşılığı yok, Java getBytes ile yazarken ? koyar. “Toplam” satırı, bu metni o kodlamayla kaydeden bir dosyanın kaç byte olacağını gösterir.

Dosyalar ve Kodlama

Bir Dosyaya Sayı (127) Yazma

  • Bir sayının dosyada nasıl saklanacağı veri türüne, kullanım amacına ve dosya biçimine bağlıdır.
  • Metin olarak: sayı önce karakterlere çevrilir; her rakam bir ASCII byte'ı tutar. Dikkat: '1' karakterinin kodu 49'dur, 1 değil.
  • İkili olarak: sayının ikilik değeri (0111 1111) türün boyutu kadar byte'a doğrudan yazılır. Sayısal veri için daha verimlidir.
Metin dosyası (ASCII): her rakam bir karakter'1' = 4900110001'2' = 5000110010'7' = 5500110111→ 3 byteİkili dosya: sayının kendisibyte01111111→ 1 byteshort0000000001111111→ 2 byteint00000000000000000000000001111111→ 4 byte

Dosyalar ve Kodlama

Metin mi, İkili mi? Karşılaştırma

127 nasıl yazıldı?Boyut
ASCII / UTF-8 metin dosyası3 byte
UTF-16 metin dosyası (BOM'suz)6 byte
İkili, byte1 byte
İkili, short2 byte
İkili, int4 byte
  • Metin: insanlar okuyabilir, her düzenleyiciyle açılır, platformlar arası kolay taşınır. Kayıt dosyaları, ayarlar, CSV tabloları.
  • İkili: sayısal veriyi daha küçük ve hızlı saklar; ama yazan programın biçimini bilmek gerekir. Resim, ses, veri tabanı dosyaları.
  • int her zaman 4 byte'tır: 7 de, 2 000 000 000 de. Metinde ise boyut basamak sayısına bağlıdır (7 → 1 byte, 2 000 000 000 → 10 byte).
Kullanım alanları: ASCII / metin, insanların okuyacağı veriler için; ikili dosyalar, sayısal verilerin verimli saklanması için kullanılır.

Java araçları: DataOutputStream ilkel türleri ikili yazar (writeByte, writeShort, writeInt…), DataInputStream aynı sırayla geri okur; OutputStreamWriter karakterleri verilen kodlamayla byte'a çevirir.

Dosyalar ve Kodlama

Kodla Doğrulama: 127

public class Sayi127 {
  static void metinYaz(String ad, Charset kod) throws IOException {
    Writer w = new OutputStreamWriter(new FileOutputStream(ad), kod);
    w.write(String.valueOf(127));            // "127": 3 karakter
    w.close();
  }
  static void ikiliYaz(String ad, int boyut) throws IOException {
    FileOutputStream fos = new FileOutputStream(ad);
    DataOutputStream d = new DataOutputStream(fos);
    if (boyut == 1) d.writeByte(127);        // 01111111
    else if (boyut == 2) d.writeShort(127);  // 00000000 01111111
    else d.writeInt(127);                    // 4 byte
    d.close();
  }
  public static void main(String[] args) throws IOException {
    metinYaz("metin.txt", StandardCharsets.US_ASCII);
    metinYaz("metin16.txt", StandardCharsets.UTF_16BE);
    ikiliYaz("b.dat", 1);  ikiliYaz("s.dat", 2);  ikiliYaz("i.dat", 4);
    String[] adlar = {"metin.txt", "metin16.txt",
             "b.dat", "s.dat", "i.dat"};
    for (String ad : adlar)
      System.out.println(ad + ": " + new File(ad).length());
  }
}
Çıktı (byte)
metin.txt: 3
metin16.txt: 6
b.dat: 1
s.dat: 2
i.dat: 4

Dosya Sistemi

java.io.File Sınıfı

  • File nesnesi diskteki bir yolu (path) temsil eder: bir dosya ya da klasör. Dosyanın içeriğini okumaz, yazmaz; bunu akışlar yapar.
  • Dosya sistemine erişip şu soruları cevaplar:
  • Yol var mı yok mu? Bir dosya mı, klasör mü?
  • Okunabilir, yazılabilir, çalıştırılabilir mi? Boyutu ve son değiştirilme zamanı ne?
  • Yeni dosya veya klasör oluşturabilir, var olanı silebilir, bir klasörün içeriğini listeleyebilir.
  • new File("ornek.txt") diskte hiçbir şey oluşturmaz; yalnızca yolu tutar.
MetotDöndürdüğü
exists()yol var mı (boolean)
isFile() / isDirectory()dosya mı / klasör mü
canRead(), canWrite(), canExecute()izinler
createNewFile()oluşturuldu mu (varsa false)
mkdir() / mkdirs()klasör (ara klasörlerle)
delete()silindi mi (dolu klasör silinmez)
list() / listFiles()String[] / File[]
length()boyut (byte, long)
lastModified()1970'ten beri milisaniye
getName(), getAbsolutePath()ad, tam yol

Dosya Sistemi

File ile Dosya ve Klasör İşlemleri

public class DosyaBilgi {
  public static void main(String[] args) throws IOException {
    File klasor = new File("ornek");
    File dosya = new File("ornek/dosya.txt");
    System.out.println("var mı: " + dosya.exists());
    System.out.println("mkdir: " + klasor.mkdir());
    System.out.println("oluştu: " + dosya.createNewFile());
    System.out.println("dosya mı: " + dosya.isFile());
    System.out.println("klasör mü: " + dosya.isDirectory());
    System.out.println("okunur: " + dosya.canRead());
    System.out.println("yazılır: " + dosya.canWrite());
    System.out.println("boyut: " + dosya.length());
    String[] icerik = klasor.list();
    System.out.println("klasörde: " + icerik.length + " öğe");
    long zaman = dosya.lastModified();
    System.out.println("zaman > 0: " + (zaman > 0));
    System.out.println("silindi: " + dosya.delete());
    System.out.println("var mı: " + dosya.exists());
    klasor.delete();
  }
}
Çıktı
var mı: false
mkdir: true
oluştu: true
dosya mı: true
klasör mü: false
okunur: true
yazılır: true
boyut: 0
klasörde: 1 öğe
zaman > 0: true
silindi: true
var mı: false
  • Yol ayırıcı olarak / her sistemde çalışır; File.separator da kullanılabilir.
  • Göreli yol, programın çalıştığı klasöre göredir.

Metin Okuma ve Yazma

Scanner ile Dosya Okuma

public class ScannerDosya {
  public static void main(String[] args) {
    Scanner scanner = null;
    try {
      scanner = new Scanner(new File("ornekDosya.txt"));
      // Dosyanın içeriğini okuma ve ekrana yazdırma
      while (scanner.hasNextLine()) {
        String satir = scanner.nextLine();
        System.out.println(satir);
      }
    } catch (FileNotFoundException e) {
      System.err.println("Dosya bulunamadı: " + e.getMessage());
    } finally {
      if (scanner != null)
        scanner.close();
    }
  }
}
Çıktı
Java
Giriş Çıkış
Akışlar
  • Dosya yoksa denetlenen bir istisna fırlatılır: catch zorunlu.
  • hasNextLine(): satır var mı? nextLine(): satırı al.
  • Dosya açılamazsa scanner null kalır: finally'deki kontrol şart.

Scanner dosya içeriğini okumak ve işlemek için kullanılır; klavye ve String gibi diğer girdileri de okur. Gereken importlar: java.io.File, java.io.FileNotFoundException, java.util.Scanner.

Metin Okuma ve Yazma

try-with-resources (Java 7+)

public class ScannerTwr {
  public static void main(String[] args) {
    try (Scanner scanner = new Scanner(new File("notlar.txt"))) {
      int toplam = 0, adet = 0;
      while (scanner.hasNextInt()) {
        toplam += scanner.nextInt();
        adet++;
      }
      double ort = (double) toplam / adet;
      System.out.println(adet + " not, ortalama " + ort);
    } catch (FileNotFoundException e) {
      System.err.println("Dosya bulunamadı: " + e.getMessage());
    }
  }
}
Çıktı (notlar.txt: 70 85 / 90 / 45 60)
5 not, ortalama 70.0
  • Parantezde açılan kaynak blok bitince otomatik kapatılır; hata olsa bile.
  • finally ve null kontrolü gerekmez.
  • Kaynak AutoCloseable olmalı: akışlar, Scanner, kanallar.
  • Birden çok kaynak ; ile ayrılır, ters sırayla kapanır.
  • nextInt() sayıları boşluk ve satır sonuna bakmadan okur.

Metin Okuma ve Yazma

Java I/O Kütüphanesi

  • java.io paketi giriş/çıkış işlemleri için güçlü ve esnek bir araç seti sunar; dosya işlemlerinden ağ programlamaya kadar kullanılır.
Giriş / çıkış

Dosya, ağ, bellek gibi kaynaklardan veri almak ve göndermek.

İkili / metin

Hem byte tabanlı (ikili) hem karakter tabanlı (metin) işlemleri destekler.

Sıralı / rasgele

Dosyadaki verilere hem sırayla hem istenen konuma atlayarak erişilebilir.

Öğe türüÖrneklerGörevi
SınıflarFiledosya ve klasör (yol) işlemleri
InputStream, OutputStreamtemel byte giriş/çıkışı
Reader, Writermetin (karakter) giriş/çıkışı
ArayüzlerCloseable, AutoCloseablekaynağı serbest bırakma: close()
Flushabletamponu boşaltma: flush()
İstisnalarIOExceptiongiriş/çıkış hatalarının üst sınıfı
FileNotFoundException, EOFExceptiondosya yok / beklenmedik dosya sonu

Metin Okuma ve Yazma

Bayt ve Karakter Odaklı Giriş Çıkış

Bayt akışları

  • InputStream ve OutputStream sınıfları
  • Veri byte (8 bit) dizileri olarak işlenir
  • Resim, ses, video, .class gibi ikili verilerle çalışır
  • read() 0–255 arası bir byte döndürür

Karakter akışları

  • Reader ve Writer sınıfları
  • Veri char (16 bit Unicode) olarak işlenir
  • Metin tabanlı verilerle daha verimli çalışır
  • Kodlamayı (UTF-8…) çözer: farklı dil ve alfabeleri destekler
Bayt akışıKarakter akışı
Okuma birimibytechar
Dosya sınıflarıFileInputStream, FileOutputStreamFileReader, FileWriter
TamponluBufferedInputStream, BufferedOutputStreamBufferedReader, BufferedWriter
Kullanımher tür dosya, kopyalamayalnızca metin dosyaları

Köprü sınıflar: InputStreamReader bir bayt akışını karakter akışına, OutputStreamWriter karakterleri bayt akışına çevirir (kodlama burada uygulanır).

Metin Okuma ve Yazma

Bayt Akışı Sınıfları

InputStreamFileInputStreamByteArrayInputStreamPipedInputStreamSequenceInputStreamObjectInputStreamFilterInputStreamBufferedInputStreamDataInputStreamOutputStreamFileOutputStreamByteArrayOutputStreamPipedOutputStreamObjectOutputStreamFilterOutputStreamBufferedOutputStreamDataOutputStreamPrintStream

İtalik kutular soyut (abstract) sınıflardır; içi boş üçgenli oklar kalıtımı gösterir. System.out bir PrintStream'dir. Filter… alt sınıfları başka bir akışı sarmalayıp yeni yetenek ekler (tampon, ilkel tür okuma).

Metin Okuma ve Yazma

Karakter Akışı Sınıfları

ReaderInputStreamReaderFileReaderBufferedReaderStringReaderCharArrayReaderPipedReaderWriterOutputStreamWriterFileWriterBufferedWriterPrintWriterStringWriterCharArrayWriterPipedWriter

FileReader aslında bir InputStreamReader'dır: FileInputStream'den gelen byte'ları seçilen kodlamayla karakterlere çevirir. FileWriter da aynı şekilde OutputStreamWriter'dan türer.

Metin Okuma ve Yazma

Sarmalayıcı (Decorator) Akışlar

BufferedReaderreadLine(), 8 KB tamponFileReaderbyte → char (kodlama)ornekDosya.txt (disk)okuma yönü: disk → programOkuma zinciriYazma zinciriPrintWriterprint, println, printfBufferedWritertamponFileWriterchar → bytedosya2.txt (disk)
BufferedReader okuyucu = new BufferedReader(new FileReader("ornekDosya.txt"));
PrintWriter yazici = new PrintWriter(new BufferedWriter(new FileWriter("dosya2.txt")));
  • Her akış sınıfı tek bir işi iyi yapar; ihtiyaç duyulan yetenekler akışları iç içe sarmalayarak birleştirilir (Decorator tasarım deseni).
  • Benzetme: hortumun ucuna önce filtre, sonra püskürtme başlığı takmak. Su (veri) hepsinden sırayla geçer.
  • En dıştaki akışı kapatmak (close()) içtekilerin hepsini de kapatır.

Metin Okuma ve Yazma

BufferedReader ile Satır Satır Okuma: Adım Adım

ornekDosya.txtJava⏎Giriş Çıkış⏎Akışlar⏎(dosya sonu)

File yalnızca yolu tutar; dosya henüz açılmadı.

ornekDosya.txtJava⏎Giriş Çıkış⏎Akışlar⏎(dosya sonu)

FileReader dosyayı açar, BufferedReader onu sarmalar. Okuma konumu dosyanın başında.

ornekDosya.txtJava⏎Giriş Çıkış⏎Akışlar⏎(dosya sonu)

readLine() satırı okur, ⏎'i atar. Sonuç null değil → döngüye gir.

ornekDosya.txtJava⏎Giriş Çıkış⏎Akışlar⏎(dosya sonu)

Satır ekrana yazılır.

ornekDosya.txtJava⏎Giriş Çıkış⏎Akışlar⏎(dosya sonu)

Sıradaki satır okunur: satir = "Giriş Çıkış".

ornekDosya.txtJava⏎Giriş Çıkış⏎Akışlar⏎(dosya sonu)

Satır ekrana yazılır.

ornekDosya.txtJava⏎Giriş Çıkış⏎Akışlar⏎(dosya sonu)

Sıradaki satır okunur: satir = "Akışlar".

ornekDosya.txtJava⏎Giriş Çıkış⏎Akışlar⏎(dosya sonu)

Satır ekrana yazılır.

ornekDosya.txtJava⏎Giriş Çıkış⏎Akışlar⏎(dosya sonu)

Dosya sonu: readLine() null döndürür → döngü biter.

ornekDosya.txtJava⏎Giriş Çıkış⏎Akışlar⏎(dosya sonu)

try biter: okuyucu otomatik kapatılır; catch çalışmaz.

import java.io.*;

public class SatirOku {
  public static void main(String[] args) {
    File dosya = new File("ornekDosya.txt");
    try (BufferedReader okuyucu =
        new BufferedReader(new FileReader(dosya))) {
      String satir;
      while ((satir = okuyucu.readLine()) != null) {
        System.out.println(satir);
      }
    } catch (IOException e) {
      System.err.println("Okuma hatası: " + e.getMessage());
      e.printStackTrace();
    }
  }
}

Metin Okuma ve Yazma

java.io.PrintWriter

public class PrintWriterOrnek {
  public static void main(String[] args) {
    PrintWriter yazici = null;
    try {
      yazici = new PrintWriter("yeniDosya.txt");
      yazici.println("Merhaba, dünya!");
      yazici.println("Bu bir örnek dosyadır.");
      yazici.printf("Pi yaklaşık %.2f%n", Math.PI);
      yazici.println(42);
    } catch (IOException e) {
      System.err.println("Dosya hatası: " + e.getMessage());
      e.printStackTrace();
    } finally {
      if (yazici != null)
        yazici.close();
    }
  }
}
  • Metin dosyalarına kolayca yazar: print, println, printf.
  • Diğer türleri metne çevirerek yazar.
  • Dosya yoksa oluşturur, varsa içini siler.
  • close unutulmamalı: veri tamponda kalabilir.
yeniDosya.txt
Merhaba, dünya!
Bu bir örnek dosyadır.
Pi yaklaşık 3.14
42

Metin Okuma ve Yazma

java.io.FileWriter

  • Dosyalara karakter tabanlı veri yazmak için kullanılır; karakter akışına (Writer) veri yazma işlevi sağlar.
  • Yalnızca write(String), write(char[]), write(int c) vardır: metin biçimlendirme yetenekleri sınırlıdır, satır sonunu (\n) kendiniz yazarsınız.
  • Daha düşük seviyeli ve esnek bir yaklaşımdır; çoğunlukla BufferedWriter veya PrintWriter ile sarmalanır.
  • new FileWriter(ad, true): dosyanın sonuna ekler (append); false veya parametresiz hâli dosyayı sıfırlar.
FileWriterBufferedWriterPrintWriter
Yazma metotlarıwritewrite, newLine()print, println, printf
Sayı yazmakönce String'e çevirönce String'e çevirdoğrudan: println(42)
Tamponküçük iç tampon8 KB tamponsardığı akışa bağlı
Hata bildirimiIOExceptionIOExceptioncheckError()

Metin Okuma ve Yazma

FileWriter Örneği: Yazma ve Ekleme

public class FileWriterOrnek {
  public static void main(String[] args) {
    try (FileWriter yazici = new FileWriter("yeniDosya.txt")) {
      yazici.write("Merhaba, dünya!\n");
      yazici.write("Bu bir FileWriter örneğidir.\n");
      System.out.println("Dosya oluşturuldu ve veri yazıldı.");
    } catch (IOException e) {
      System.err.println("Dosya hatası: " + e.getMessage());
      e.printStackTrace();
    }
    // true: dosyanın sonuna ekle (append)
    try (FileWriter ekle = new FileWriter("yeniDosya.txt", true)) {
      ekle.write("Sona eklenen satır.\n");
    } catch (IOException e) {
      System.err.println("Dosya hatası: " + e.getMessage());
    }
  }
}
Çıktı
Dosya oluşturuldu ve veri yazıldı.
yeniDosya.txt
Merhaba, dünya!
Bu bir FileWriter örneğidir.
Sona eklenen satır.

Metin Okuma ve Yazma

Karakter Kodlamasını Belirtmek

public class KodlamaOku {
  public static void main(String[] args) throws IOException {
    File dosya = new File("turkce.txt");
    try (BufferedWriter yazici = new BufferedWriter(
        new FileWriter(dosya, StandardCharsets.UTF_8))) {    // Java 11+
      yazici.write("Işık, şimşek, çiğ, öğrenci, Ünye");
    }
    try (BufferedReader dogru = new BufferedReader(
        new FileReader(dosya, StandardCharsets.UTF_8))) {
      System.out.println(dogru.readLine());
    }
    try (BufferedReader yanlis = new BufferedReader(
        new FileReader(dosya, StandardCharsets.ISO_8859_1))) {
      System.out.println(yanlis.readLine());
    }
  }
}
Çıktı
Işık, şimşek, çiğ, öğrenci, Ünye
IÅık, ÅimÅek, çiÄ, öÄrenci, Ãnye
  • Kodlama verilmezse varsayılan kullanılır: Java 18+ UTF-8, eskiden işletim sisteminin ayarı (Windows-1254). Taşınabilir kod için kodlamayı açıkça yazın; Java 10 ve öncesinde InputStreamReader köprüsüyle.

Tamponlama

Tamponlama (Buffering)

  • Temel metotlar: read() bir byte veya byte dizisi okur, write() bir byte veya byte dizisi yazar.
  • Her byte için ayrı disk erişimi uygulamayı ciddi şekilde yavaşlatır: disk, bellekten binlerce kat yavaştır.
  • Çözüm: veriyi bellekte bir tamponda (buffer) biriktirip diskten okuma / diske yazma işlemlerini toplu yapmak. Fiziksel disk erişimi sayısı azalır.
Benzetme: marketten 10 ürünü tek tek, her biri için ayrı yolculukla taşımak yerine bir sepete doldurup tek seferde taşımak.
ProgramDisk5 × write(1 byte)5 byte → 5 disk erişimi (her biri yavaş)

Tamponsuz yazma: her write() çağrısı doğrudan diske gider.

ProgramDisktampon (RAM)BBBBBwrite()5 write() → bellekte birikti, disk erişimi yok

Tamponlu yazma: write() çağrıları byte'ları önce bellekteki tampona koyar.

ProgramDisktampon (RAM)BBBBBwrite()flush()flush / close / tampon doldu → 1 disk erişimi

Tampon dolunca, flush() veya close() çağrılınca tamponun tamamı tek seferde diske yazılır.

Tamponlama

Tamponlu Akış Sınıfları

SınıfSardığı akışNe yapar?
BufferedInputStreamInputStreamokumayı hızlandırır: read() çağrıldığında diskten tek seferde 8192 byte'ı bellekteki önbelleğe alır
BufferedReaderReaderaynısı, karakterler için; ayrıca readLine()
BufferedOutputStreamOutputStreamyazmayı hızlandırır: write() verisini önbellekte biriktirir
BufferedWriterWriteraynısı, karakterler için; ayrıca newLine()
public class FlushOrnek {
  public static void main(String[] args) throws IOException {
    File log = new File("log.txt");
    BufferedWriter bw = new BufferedWriter(new FileWriter(log));
    bw.write("Kayıt 1");
    System.out.println("önce: " + log.length());
    bw.flush();
    System.out.println("sonra: " + log.length());
    bw.close();
  }
}
Çıktı
önce: 0
sonra: 8

flush() tamponu hemen diske boşaltır; close() önce flush() yapar. Kapatılmayan tamponlu yazıcıda son veriler kaybolur.

Tamponlama

BufferedInputStream ve BufferedReader

try {
  FileInputStream fis = new FileInputStream("dosya.txt");
  BufferedInputStream bis = new BufferedInputStream(fis);
  int veri;
  while ((veri = bis.read()) != -1) {
    System.out.print((char) veri);
  }
  bis.close();     // içteki fis'i de kapatır
} catch (IOException e) {
  System.err.println("Dosya hatası: " + e.getMessage());
}
Çıktı: byte byte
Java I/O
Tampon
try {
  FileReader fr = new FileReader("dosya.txt");
  BufferedReader br = new BufferedReader(fr);
  String satir;
  while ((satir = br.readLine()) != null) {
    System.out.println(satir);
  }
  br.close();     // içteki fr'yi de kapatır
} catch (IOException e) {
  System.err.println("Dosya hatası: " + e.getMessage());
}
Çıktı: satır satır
Java I/O
Tampon

Tamponlama

Tamponlama Ne Kadar Hızlandırır?

public class TamponOlcum {
  static long oku(InputStream in) throws IOException {
    long bas = System.nanoTime();
    while (in.read() != -1) { }          // her seferinde 1 byte
    in.close();
    return (System.nanoTime() - bas) / 1_000_000;
  }
  public static void main(String[] args) throws IOException {
    File f = new File("buyuk.dat");
    try (FileOutputStream fos = new FileOutputStream(f)) {
      fos.write(new byte[2 * 1024 * 1024]);   // 2 MB
    }
    long t1 = oku(new FileInputStream(f));
    long t2 = oku(new BufferedInputStream(new FileInputStream(f)));
    System.out.println("Tamponsuz: " + t1 + " ms");
    System.out.println("Tamponlu : " + t2 + " ms");
    f.delete();
  }
}
Çıktı (örnek çalıştırma)
Tamponsuz: 3151 ms
Tamponlu : 94 ms
  • Aynı 2 MB dosya, iki kez byte byte okunuyor.
  • Tamponsuz: 2 097 152 read() = o kadar sistem çağrısı.
  • Tamponlu: sistem çağrısı 8 KB'ta bir, 256 kez. Fark onlarca kat.
  • Süreler makineye göre değişir; oran benzer kalır.

Diğer Bayt Akışları

java.io.FileInputStream

  • InputStream'dan türer; dosyadan doğrudan byte okur. Düşük seviyelidir: resim, ses gibi metin dışı veriler için.
  • read(): tek byte (0–255), dosya sonunda −1. read(byte[] b): en fazla b.length byte okur ve okuduğu sayıyı döndürür.
  • Metin için zordur (ü gibi çok byte'lı karakterler bölünebilir); FileReader gibi karakter tabanlı sınıflar daha uygundur.
public class ParcaOku {
  public static void main(String[] args) throws IOException {
    try (FileInputStream fis = new FileInputStream("dosya.txt")) {
      byte[] dizi = new byte[4];
      int adet;
      while ((adet = fis.read(dizi)) != -1) {
        System.out.println(adet + ": " + new String(dizi, 0, adet));
      }
    }
  }
}
Çıktı
4: Merh
4: aba 
4: Java

dosya.txt: “Merhaba Java”, 12 byte, 4'erli parçalar. new String(dizi, 0, adet) yalnızca okunan kısmı çevirir.

Diğer Bayt Akışları

java.io.ByteArrayInputStream

  • InputStream'dan türer; bellekteki bir byte dizisinden okur. Fiziksel kaynağa erişmez, veri bellekte olduğu için hızlıdır.
  • Yalnızca okunur; belleğe yazmak için eşi ByteArrayOutputStream var. Büyük veride bellek tüketimi sorun olabilir.
public class ByteDiziOku {
  public static void main(String[] args) throws IOException {
    byte[] dizi = {74, 97, 118, 97};           // "Java"
    ByteArrayInputStream bais = new ByteArrayInputStream(dizi);
    int b;
    while ((b = bais.read()) != -1) {
      System.out.print(b + "=" + (char) b + " ");
    }
    System.out.println();
    // Belleğe yazma: ByteArrayOutputStream
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    baos.write(dizi);
    baos.write('!');
    byte[] sonuc = baos.toByteArray();
    System.out.println(sonuc.length + " byte: " + baos);
  }
}
Çıktı
74=J 97=a 118=v 97=a 
5 byte: Java!

Diğer Bayt Akışları

StringBufferInputStream: Kullanmayın

  • java.io.StringBufferInputStream Java 1.1'den beri kullanımdan kaldırılmıştır (deprecated); kullanımı tavsiye edilmez.
  • Karakterleri byte'a doğru çevirmez (yalnızca alt 8 bit). Yerine ByteArrayInputStream + getBytes(UTF_8) veya StringReader.
public class EskiAkis {
  @SuppressWarnings("deprecation")
  public static void main(String[] args) throws IOException {
    InputStream eski = new StringBufferInputStream("ş€");
    int b;
    while ((b = eski.read()) != -1) {
      System.out.print(b + " ");
    }
    System.out.println();
    byte[] dogru = "ş€".getBytes("UTF-8");
    InputStream yeni = new ByteArrayInputStream(dogru);
    while ((b = yeni.read()) != -1) {
      System.out.print(b + " ");
    }
    System.out.println();
  }
}
Çıktı
95 172 
197 159 226 130 172 

ş (351) → 95 = _, € → 172. Doğrusu UTF-8: 197 159 226 130 172.

Diğer Bayt Akışları

PipedInputStream ve PipedOutputStream

  • InputStream / OutputStream sınıflarından türetilmiştir; iki iş parçacığı (thread) arasında güvenli veri iletişimi sağlar.
  • Bir iş parçacığı PipedOutputStream ile veri yazar, diğeri PipedInputStream ile bu veriyi okur. İki uç new PipedOutputStream(pis) ile bağlanır.
  • Okuma ve yazma senkronizedir: boru boşsa okuyucu bekler, doluysa yazıcı bekler.
  • Yalnızca tek yönlü veri iletimini destekler; çift yön için iki boru gerekir.
  • İki ucu aynı iş parçacığında kullanmak kilitlenmeye (deadlock) yol açabilir. İş parçacıkları Bölüm 9'da ayrıntılı işlenecek.
Yazıcı iş parçacığıpos.write(…)PipedOutputStreamOkuyucu iş parçacığıpis.read()PipedInputStreamMerhababoru: 1024 byte'lık dairesel tampon, tek yön

Diğer Bayt Akışları

Boru Örneği

PipedInputStream pis = new PipedInputStream();
PipedOutputStream pos = new PipedOutputStream(pis);
// Okuyucu iş parçacığı
Thread okuyucu = new Thread(() -> {
  try {
    int b;
    while ((b = pis.read()) != -1)
      System.out.print((char) b);
    System.out.println();
    pis.close();
  } catch (IOException e) {
    e.printStackTrace();
  }
});
// Yazıcı iş parçacığı
Thread yazici = new Thread(() -> {
  try {
    pos.write("Merhaba, boru!".getBytes());
    pos.close();
  } catch (IOException e) {
    e.printStackTrace();
  }
});
okuyucu.start();
yazici.start();
yazici.join();
okuyucu.join();
Çıktı
Merhaba, boru!
  • start() iş parçacığını başlatır; join() bitmesini bekler.
  • Yazıcı pos.close() deyince okuyucunun read()'i −1 döner, döngü biter.

Diğer Bayt Akışları

java.io.SequenceInputStream

  • Birden fazla InputStream'i birleştirerek ardışık tek bir giriş akışı oluşturur: birinci akış bitince (−1) ikinciye geçer.
  • Farklı kaynaklardan gelen veriyi tek akışta birleştirir. Bir akışta hata olursa IOException fırlatılır, diğerine geçilmez.
dosya1.txtdosya2.txtSequenceInputStream1. dosya2. dosyatek, ardışık akış
try {
  FileInputStream is1 = new FileInputStream("dosya1.txt");
  FileInputStream is2 = new FileInputStream("dosya2.txt");
  SequenceInputStream sis = new SequenceInputStream(is1, is2);
  int okunanByte;
  while ((okunanByte = sis.read()) != -1)
    System.out.print((char) okunanByte);
  sis.close();     // is1 ve is2'yi de kapatır
} catch (IOException e) {
  System.err.println("Okuma hatası: " + e.getMessage());
}
Çıktı
Birinci dosya.
Ikinci dosya.

sis.close() içteki akışları da kapatır.

Dosya Kopyalama

Bayt Bayt Dosya Kopyalama

  • Kaynak dosyadan okunan her byte'ı hedef dosyaya yazar: her tür dosyayı (metin, resim, .exe) bozmadan kopyalar.
  • BufferedInputStream ve BufferedOutputStream giriş ve çıkışı hızlandırır: önbellek kullanarak okuma ve yazmayı toplu yapar.
  • bis.read(): bir byte okur, dosyanın sonunda −1 döner. bos.write(veri): okunan byte'ı çıkış dosyasına yazar.
  • Okunan değer int bir değişkende tutulmalıdır: −1 ile 255 byte değerini ayırt etmek için.
dosya.txtFileInputStreamBufferedInputStreamveriBufferedOutputStreamFileOutputStreamdosya2.txtread()write(veri)

Kopyalama döngüsü tek satırdır: while ((veri = bis.read()) != -1) bos.write(veri);

Dosya Kopyalama

Bayt Bayt Kopyalama: Adım Adım

dosya.txt (disk)Javabis tamponu (bellek)…veri = ?bos tamponu (bellek)…dosya2.txt (disk)

İki File nesnesi yalnızca yolları tutar.

dosya.txt (disk)Javabis tamponu (bellek)…veri = ?bos tamponu (bellek)…dosya2.txt (disk)0 byte

FileOutputStream hedef dosyayı oluşturur (varsa içini siler): 0 byte.

dosya.txt (disk)Javabis tamponu (bellek)…veri = ?bos tamponu (bellek)…dosya2.txt (disk)0 byte

İki akış tamponlu akışlarla sarmalanır; tamponlar (8192 byte) boş.

dosya.txt (disk)Javabis tamponu (bellek)Java…veri = 74 'J'bos tamponu (bellek)…dosya2.txt (disk)0 byte

İlk read(): tampon boş olduğu için diskten tek seferde 4 byte'ın tamamı tampona alınır; ilk byte 74 ('J') döner.

dosya.txt (disk)Javabis tamponu (bellek)Java…veri = 74 'J'bos tamponu (bellek)J…dosya2.txt (disk)0 byte

write byte'ı çıkış tamponuna koyar; dosya2.txt hâlâ 0 byte.

dosya.txt (disk)Javabis tamponu (bellek)Java…veri = 97 'a'bos tamponu (bellek)J…dosya2.txt (disk)0 byte

Bu kez diske gidilmez: byte tampondan gelir (97 = 'a').

dosya.txt (disk)Javabis tamponu (bellek)Java…veri = 97 'a'bos tamponu (bellek)Ja…dosya2.txt (disk)0 byte

Çıkış tamponunda artık 2 byte var.

dosya.txt (disk)Javabis tamponu (bellek)Java…veri = 118 'v'bos tamponu (bellek)Ja…dosya2.txt (disk)0 byte

Bu kez diske gidilmez: byte tampondan gelir (118 = 'v').

dosya.txt (disk)Javabis tamponu (bellek)Java…veri = 118 'v'bos tamponu (bellek)Jav…dosya2.txt (disk)0 byte

Çıkış tamponunda artık 3 byte var.

dosya.txt (disk)Javabis tamponu (bellek)Java…veri = 97 'a'bos tamponu (bellek)Jav…dosya2.txt (disk)0 byte

Bu kez diske gidilmez: byte tampondan gelir (97 = 'a').

dosya.txt (disk)Javabis tamponu (bellek)Java…veri = 97 'a'bos tamponu (bellek)Java…dosya2.txt (disk)0 byte

Çıkış tamponunda artık 4 byte var.

dosya.txt (disk)Javabis tamponu (bellek)Java…veri = -1bos tamponu (bellek)Java…dosya2.txt (disk)0 byte

Okunacak byte kalmadı: read() −1 döndürür, döngü biter.

dosya.txt (disk)Javabis tamponu (bellek)Java…veri = -1bos tamponu (bellek)…dosya2.txt (disk)Java

close() önce flush() yapar: 4 byte tek seferde diske yazılır, sonra fos kapanır.

dosya.txt (disk)Javabis tamponu (bellek)Java…veri = -1bos tamponu (bellek)…dosya2.txt (disk)Java

Giriş akışları da kapatılır.

dosya.txt (disk)Javabis tamponu (bellek)Java…veri = -1bos tamponu (bellek)…dosya2.txt (disk)Java

Diskte 1 okuma ve 1 yazma oldu; tamponsuz olsaydı 5 okuma ve 4 yazma olurdu.

import java.io.*;

public class BaytKopya {
  public static void main(String[] args) throws IOException {
    File dosya = new File("dosya.txt");
    File dosya2 = new File("dosya2.txt");
    FileInputStream fis = new FileInputStream(dosya);
    FileOutputStream fos = new FileOutputStream(dosya2);
    BufferedInputStream bis = new BufferedInputStream(fis);
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    int veri;
    // Bir byte oku. Dosyanın sonunda -1 dönecektir.
    while ((veri = bis.read()) != -1)
      bos.write(veri);    // Okunan byte'ı çıkışa yaz
    bos.close();
    bis.close();
    System.out.println(dosya2.length() + " byte kopyalandı");
  }
}

Dosya Kopyalama

Blok Blok (Parça Parça) Kopyalama

public class ParcaKopya {
  public static void main(String[] args) throws IOException {
    // Veri, 16K'lık parçalar hâlinde okunacak
    byte[] parca = new byte[1024 * 16];
    File dosya = new File("dosya.txt");
    File dosya2 = new File("dosya2.txt");
    FileInputStream fis = new FileInputStream(dosya);
    FileOutputStream fos = new FileOutputStream(dosya2);
    BufferedInputStream bis = new BufferedInputStream(fis);
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    int boyut;
    // 16K'ya kadar oku. Dosyanın sonunda -1 dönecektir.
    while ((boyut = bis.read(parca)) > -1)
      bos.write(parca, 0, boyut);   // Okunan parçayı çıkışa yaz
    bos.close();
    bis.close();
    System.out.println(dosya2.length() + " byte kopyalandı");
  }
}
Çıktı (40 000 byte)
40000 byte kopyalandı
  • Toplu okuma performansı artırır.
  • read(parca): okunan byte sayısını döndürür.
  • write(parca, 0, boyut): yalnızca ilk boyut byte'ı yazar.

new byte[1024 * 16]: veri 16 KB'lık parçalar hâlinde işlenir; dosya ne kadar büyük olursa olsun bellek kullanımı sabit kalır.

Dosya Kopyalama

Blok Blok Kopyalama: Adım Adım

dosya.txt: 39 byte, 16'lık bloklar0–1516–3132–38parca dizisi (16 eleman)boyut = ?

16 byte'lık bir dizi (gerçek programlarda 8–16 KB).

dosya.txt: 39 byte, 16'lık bloklar0–1516–3132–38parca dizisi (16 eleman)boyut = ?

İki kaynak birlikte açılır; try bitince ikisi de kapanacak.

dosya.txt: 39 byte, 16'lık bloklar0–1516–3132–38parca dizisi (16 eleman)GirisCikisveAboyut = 16

read(parca) diziyi doldurur, 16 döner.

dosya.txt: 39 byte, 16'lık bloklar0–1516–3132–38parca dizisi (16 eleman)GirisCikisveAboyut = 16

write(parca, 0, 16): yalnızca okunan 16 byte yazılır.

dosya.txt: 39 byte, 16'lık bloklar0–1516–3132–38parca dizisi (16 eleman)GirisCikisveAboyut = 16

Okunan miktar yazdırılır.

dosya.txt: 39 byte, 16'lık bloklar0–1516–3132–38parca dizisi (16 eleman)kislar-JavaDeboyut = 16

Sonraki 16 byte aynı dizinin üzerine yazılır.

dosya.txt: 39 byte, 16'lık bloklar0–1516–3132–38parca dizisi (16 eleman)kislar-JavaDeboyut = 16

write(parca, 0, 16): yalnızca okunan 16 byte yazılır.

dosya.txt: 39 byte, 16'lık bloklar0–1516–3132–38parca dizisi (16 eleman)kislar-JavaDeboyut = 16

Okunan miktar yazdırılır.

dosya.txt: 39 byte, 16'lık bloklar0–1516–3132–38parca dizisi (16 eleman)rsi!!!-JavaDeeski veri!boyut = 7

Yalnızca 7 byte kaldı: dizinin ilk 7 elemanı yenilenir, kalan 9'u önceki bloktan kalma!

dosya.txt: 39 byte, 16'lık bloklar0–1516–3132–38parca dizisi (16 eleman)rsi!!!-JavaDeeski veri!boyut = 7

write(parca, 0, 7): yalnızca okunan 7 byte yazılır. write(parca) deseydik eski 9 byte da yazılır, kopya 48 byte olurdu.

dosya.txt: 39 byte, 16'lık bloklar0–1516–3132–38parca dizisi (16 eleman)rsi!!!-JavaDeeski veri!boyut = 7

Okunan miktar yazdırılır.

dosya.txt: 39 byte, 16'lık bloklar0–1516–3132–38parca dizisi (16 eleman)rsi!!!-JavaDeeski veri!boyut = -1

Dosya sonu: −1 döner, > -1 yanlış, döngü biter.

dosya.txt: 39 byte, 16'lık bloklar0–1516–3132–38parca dizisi (16 eleman)rsi!!!-JavaDeeski veri!boyut = -1

Kaynaklar kapandı; kopya kaynakla aynı boyutta: 16 + 16 + 7 = 39 byte.

public class ParcaIzle {
  public static void main(String[] args) throws IOException {
    byte[] parca = new byte[16];
    try (InputStream in = new FileInputStream("dosya.txt");
      OutputStream out = new FileOutputStream("dosya2.txt")) {
      int boyut;
      while ((boyut = in.read(parca)) > -1) {
        out.write(parca, 0, boyut);
        System.out.println("boyut = " + boyut);
      }
    }
    long n = new File("dosya2.txt").length();
    System.out.println("Bitti: " + n + " byte");
  }
}

Dosya Kopyalama

Satır Satır Dosya Kopyalama

public class SatirKopya {
  public static void main(String[] args) throws IOException {
    File dosya = new File("dosya.txt");
    File dosya2 = new File("dosya2.txt");
    FileReader fr = new FileReader(dosya);
    BufferedReader br = new BufferedReader(fr);
    FileWriter fw = new FileWriter(dosya2);
    BufferedWriter bw = new BufferedWriter(fw);
    PrintWriter pw = new PrintWriter(bw);
    String satir;
    // dosya sonuna kadar bir satır oku
    while ((satir = br.readLine()) != null)
      pw.println(satir);    // satırı yaz
    pw.close();
    br.close();
    System.out.println("Kopyalandı: " + dosya2.getName());
  }
}
Çıktı
Kopyalandı: dosya2.txt
  • Karakter akışlarıyla çalışır: yalnızca metin dosyaları için.
  • readLine() satır sonunu atar; println sistemin satır sonunu ekler (Windows: \r\n, Linux: \n). Kopya byte byte aynı olmayabilir.
  • pw.close() zincirin tamamını (bw, fw) kapatır.

Dosya Kopyalama

Web Sayfası İndirme

public class WebIndir {
  public static void main(String[] args) throws IOException {
    URL url = URI.create("https://sercankulcu.github.io/").toURL();
    BufferedInputStream bis = new BufferedInputStream(url.openStream());
    FileOutputStream fos = new FileOutputStream(new File("kopya.html"));
    BufferedOutputStream bos = new BufferedOutputStream(fos);
    for (int c = bis.read(); c != -1; c = bis.read()) {
      bos.write(c);
    }
    bis.close();
    bos.close();
    System.out.println("kopya.html: " + new File("kopya.html").length() + " byte");
  }
}
Çıktı (boyut sayfaya göre değişir)
  • Kaynak bir dosya değil, ağ: url.openStream() bir InputStream döndürür; kopyalama döngüsü aynıdır.
  • new URL(String) Java 20'den beri kullanımdan kaldırıldı; URI.create(…).toURL() önerilir.
  • for döngüsü: oku, −1 değilse yaz, tekrar oku.

Dosya Kopyalama

Kısa Yollar: transferTo ve Files.copy

public class KopyaKisa {
  public static void main(String[] args) throws IOException {
    // Java 9+: transferTo
    try (InputStream in = new FileInputStream("dosya.txt");
      OutputStream out = new FileOutputStream("dosya2.txt")) {
      long adet = in.transferTo(out);
      System.out.println(adet + " byte aktarıldı");
    }
    // Java 7+: Files.copy
    Files.copy(Path.of("dosya.txt"), Path.of("dosya3.txt"),
         StandardCopyOption.REPLACE_EXISTING);
    System.out.println(Files.size(Path.of("dosya3.txt")) + " byte kopyalandı");
  }
}
Çıktı
4 byte aktarıldı
4 byte kopyalandı

Kütüphane metotları içeride blok kopyalama yapar. Files.copy hedef varsa REPLACE_EXISTING seçeneği olmadan FileAlreadyExistsException fırlatır.

Dosya Kopyalama

Kopyalama Yöntemleri

YöntemBirimUygun olduğu durum
Bayt bayt (read()/write(int))1 byteöğrenmek için; tamponsuz çok yavaş
Blok blok (read(byte[]))8–16 KBher tür dosya, hızlı
Satır satır (readLine/println)1 satıryalnızca metin; satırları işlerken
in.transferTo(out) (Java 9+)blokiki akış arasında tek satırda kopya
Files.copy(kaynak, hedef) (Java 7+)blokdosyadan dosyaya en kısa yol
Hangi yöntem seçilirse seçilsin: tamponlu çalışın, akışları kapatın (tercihen try-with-resources) ve IOException'ı ele alın.

Rasgele Erişim

Dosya Erişimi: Sıralı ve Rasgele

Sıralı erişim (sequential)

  • Her okuma/yazma bir sonraki konumdan devam eder; geri dönülemez.
  • Dosya içeriğinin yapısı bilinmediğinde veya kopya oluşturulurken kullanılır.
  • Akışların (FileInputStream, BufferedReader…) hepsi sıralıdır.

Rasgele erişim (random access)

  • Dosya içinde istenen konuma hareket edilip oradan okunur/yazılır.
  • Kayıtların boyutu ve konumu biliniyorsa kullanılabilir: k. kayıt = k × kayıt boyutu.
  • Belirli bir kaydı okumak veya yerinde değiştirmek için kullanışlıdır.
Sıralı erişim: kaset gibi, baştan sona0123456789Rasgele erişim: plak gibi, istenen yere atla0123456789seek(2)seek(7)

Rasgele Erişim

java.io.RandomAccessFile

ÜyeGörevi
RandomAccessFile(String ad, String mod)mod "r": okuma, "rw": okuma ve yazma
seek(long konum)dosya işaretçisini belirtilen konuma taşır
getFilePointer()işaretçinin dosya başına uzaklığı (byte)
length()dosyanın uzunluğu (byte)
read() / write(int b)bir byte okur (int olarak döndürür) / yazar
readInt(), writeInt(int), readDouble()…ilkel türleri ikili okur/yazar (int: 4 byte)
close()sistem kaynaklarını serbest bırakır

Akışlardan farklı olarak tek bir nesne hem okur hem yazar; InputStream / OutputStream hiyerarşisinde değildir.

Rasgele Erişim

Sıralı ve Rasgele Erişim: Kod

public class RasgeleErisim {
  public static void main(String[] args) throws IOException {
    // Sıralı Erişim
    FileInputStream sirali = new FileInputStream("dosya");
    int bayt = sirali.read();
    sirali.close();
    System.out.println("İlk byte: " + bayt);
    // Rasgele Erişim
    RandomAccessFile rast = new RandomAccessFile("dosya", "rw");
    // Dosyanın başından 100 byte ilerideki konuma git
    rast.seek(100);
    // 100. konumdan itibaren 4 byte'lık bir int oku
    int veri = rast.readInt();
    System.out.println("100. konumdaki int: " + veri);
    rast.close();
  }
}
Çıktı
İlk byte: 1
100. konumdaki int: 256

seek(100) işaretçiyi 100 konumuna, yani 101. byte'a taşır (konumlar 0'dan başlar). Oradaki 4 byte 00 00 01 00 olduğundan readInt() = 256 döner. dosya burada 124 byte'lık bir veri dosyasıdır.

Rasgele Erişim

Kayıtlara Rasgele Erişim: Adım Adım

n.dat: her kayıt 4 byte (int)0–34–78–1112–1516–19işaretçi = 0

"rw": dosya yoksa oluşturulur. İşaretçi 0'da.

n.dat: her kayıt 4 byte (int)0–34–78–1112–1516–19işaretçi = 0

Yazılacak beş not.

n.dat: her kayıt 4 byte (int)700–34–78–1112–1516–19işaretçi = 4

Her writeInt 4 byte yazar ve işaretçiyi 4 ilerletir.

n.dat: her kayıt 4 byte (int)700–3854–7908–114512–156016–19işaretçi = 20

Döngü bitti: 5 × 4 = 20 byte, işaretçi dosya sonunda (20).

n.dat: her kayıt 4 byte (int)700–3854–7908–114512–156016–19işaretçi = 20

length() = 20.

n.dat: her kayıt 4 byte (int)700–3854–7908–114512–156016–19işaretçi = 8

3. kayıt (indeks 2) 8. byte'ta başlar: doğrudan oraya atlanır, öncekiler okunmaz.

n.dat: her kayıt 4 byte (int)700–3854–7908–114512–156016–19işaretçi = 12

readInt() 8–11. byte'ları okur: 90. İşaretçi 12'ye ilerler.

n.dat: her kayıt 4 byte (int)700–3854–7908–114512–156016–19işaretçi = 12

getFilePointer() = 12.

n.dat: her kayıt 4 byte (int)700–3854–7908–114512–156016–19işaretçi = 12

4. kayıt da 12'de başlıyor; işaretçi zaten orada.

n.dat: her kayıt 4 byte (int)700–3854–7908–115012–156016–19işaretçi = 16

45 yerine 50 yerinde yazılır; dosyanın geri kalanı değişmez. Sıralı akışla bu yapılamaz.

n.dat: her kayıt 4 byte (int)700–3854–7908–115012–156016–19işaretçi = 0

Başa dönülür.

n.dat: her kayıt 4 byte (int)700–3854–7908–115012–156016–19işaretçi = 20

İşaretçi uzunluğa ulaşana kadar okunur: 70 + 85 + 90 + 50 + 60 = 355.

n.dat: her kayıt 4 byte (int)700–3854–7908–115012–156016–19işaretçi = 20

Sonuç yazdırılır; ardından dosya kapatılır.

import java.io.*;

public class Kayitlar {
  public static void main(String[] args) throws IOException {
    RandomAccessFile f = new RandomAccessFile("n.dat", "rw");
    int[] notlar = {70, 85, 90, 45, 60};
    for (int n : notlar)
      f.writeInt(n);              // her kayıt 4 byte
    System.out.println("Uzunluk: " + f.length());
    f.seek(2 * 4);                  // 3. kayda git
    System.out.println("3. not: " + f.readInt());
    System.out.println("Konum: " + f.getFilePointer());
    f.seek(3 * 4);                  // 4. kayıt
    f.writeInt(50);                 // 45 yerine 50
    f.seek(0);
    int toplam = 0;
    while (f.getFilePointer() < f.length())
      toplam += f.readInt();
    System.out.println("Toplam: " + toplam);
    f.close();
  }
}

Serileştirme

Serileştirme (Serialization)

  • Serileştirme: bir nesnenin durumunu (alan değerlerini) byte dizisine çevirmek; bu sayede nesne dosyaya yazılabilir, ağ üzerinden başka bir ortama taşınabilir. Tersi serileştirmeyi geri alma (deserialization).
  • Bir sınıfın nesneleri serileştirilebilmesi için sınıf java.io.Serializable arayüzünü gerçeklemelidir (metodu olmayan bir işaret arayüzü).
  • Sınıfın sürüm kimliği serialVersionUID ile belirtilmesi önerilir; yazılmazsa derleyici sınıf yapısından üretir ve sınıf değişince eski dosyalar okunamaz.
  • static alanlar (sınıfa aittir) ve transient (geçici) olarak işaretlenen alanlar serileştirilmez; şifre gibi alanları dışarıda bırakmak için kullanılır.
Kullanım alanları: oyun kayıtları, önbellekler, nesnelerin ağ üzerinden başka bir JVM'e gönderilmesi. Uzun süreli ve diller arası veri saklamak için JSON gibi metin biçimleri daha çok tercih edilir.

Serileştirme

Serileştirme Akışı

Ogrenci nesnesiad = "Ahmet"yas = 20bellek (heap)

Bellekte bir Ogrenci nesnesi var.

Ogrenci nesnesiad = "Ahmet"yas = 20bellek (heap)writeObjectAC ED 00 05 73 72 00 07byte dizisi (72 byte)ogrenci.ser(disk)

ObjectOutputStream.writeObject nesneyi byte'lara çevirir; dosya AC ED sihirli sayısıyla başlar.

Ogrenci nesnesiad = "Ahmet"yas = 20bellek (heap)writeObjectAC ED 00 05 73 72 00 07byte dizisi (72 byte)ogrenci.ser(disk)readObjectyeni Ogrenci nesnesiad = "Ahmet"yas = 20aynı durum, farklı nesne

ObjectInputStream.readObject byte'lardan yeni bir nesne kurar: aynı değerler, farklı nesne.

Serileştirme

Serileştirilebilir Sınıf

import java.io.Serializable;

class Ogrenci implements Serializable {
  private static final long serialVersionUID = 1L;
  private String ad;
  private int yas;

  public Ogrenci(String ad, int yas) {
    this.ad = ad;
    this.yas = yas;
  }

  public String toString() {
    return "Ogrenci [ad=" + ad + ", yas=" + yas + "]";
  }
}
  • implements Serializable: metodu olmayan bir işaret arayüzü.
  • serialVersionUID: sınıfın sürüm numarası. Yazan ve okuyan sürüm farklıysa InvalidClassException.
  • Alanlar da serileştirilebilir olmalı; değilse NotSerializableException.
  • readObject Object döndürür: (Ogrenci) dönüşümü gerekir.

Serileştirme

Nesneyi Yazma ve Geri Okuma

// Serileştirme işlemi
try (ObjectOutputStream cikti = new ObjectOutputStream(
    new FileOutputStream("ogrenci.ser"))) {
  Ogrenci ogrenci = new Ogrenci("Ahmet", 20);
  cikti.writeObject(ogrenci);
  System.out.println("Serileştirme başarılı.");
} catch (IOException e) {
  e.printStackTrace();
}
System.out.println("ogrenci.ser: " + new File("ogrenci.ser").length() + " byte");
// Seriyi okuma işlemi
try (ObjectInputStream girdi = new ObjectInputStream(
    new FileInputStream("ogrenci.ser"))) {
  Ogrenci ogrenci = (Ogrenci) girdi.readObject();
  System.out.println("Ogrenci: " + ogrenci);
} catch (IOException | ClassNotFoundException e) {
  e.printStackTrace();
}
Çıktı
Serileştirme başarılı.
ogrenci.ser: 72 byte
Ogrenci: Ogrenci [ad=Ahmet, yas=20]

Serileştirme

transient ve static Alanlar

class Kullanici implements Serializable {
  private static final long serialVersionUID = 1L;
  static int sayac;
  private String ad;
  private transient String sifre;   // yazılmaz

  Kullanici(String ad, String sifre) {
    this.ad = ad;
    this.sifre = sifre;
  }

  public String toString() {
    return "Kullanici [ad=" + ad + ", sifre=" + sifre + "]";
  }
}
  • transient: bu alan serileştirmede atlanır; geri okunduğunda varsayılan değeri alır (null, 0, false).
  • Şifre, geçici önbellek, açık dosya/bağlantı gibi saklanmaması gereken veya saklanamayan alanlar için.
  • static alanlar nesneye değil sınıfa aittir: dosyaya hiç yazılmaz.

Serileştirme

transient ve static: Deneme

Kullanici k = new Kullanici("ayse", "gizli123");
Kullanici.sayac = 5;
ObjectOutputStream c = new ObjectOutputStream(
    new FileOutputStream("k.ser"));
c.writeObject(k);
c.close();
Kullanici.sayac = 99;        // static: sınıfa ait
ObjectInputStream g = new ObjectInputStream(
    new FileInputStream("k.ser"));
Kullanici okunan = (Kullanici) g.readObject();
g.close();
System.out.println(okunan);
System.out.println("sayac = " + Kullanici.sayac);
System.out.println("aynı nesne mi? " + (okunan == k));
Çıktı
Kullanici [ad=ayse, sifre=null]
sayac = 99
aynı nesne mi? false
  • sifre → null: yazılmadı.
  • sayac → 99: bellekteki son değer.
  • == yanlış: okunan, yeni bir nesne.

NIO

NIO (New I/O)

  • Java NIO (java.nio, Java 1.4+), gelişmiş G/Ç işlemleri için güçlü bir API sunar: tampon (buffer), kanal (channel), seçici (selector) ve dosya sistemi entegrasyonu içerir.
  • Klasik java.io akışlarına göre performans ve esneklik sağlar.
Buffer

Veriyi geçici olarak depolayan bellek alanları.

Channel

Giriş/çıkış işlemlerini yöneten, çift yönlü bağlantı nesneleri.

Selector

Birden çok kanalı tek iş parçacığında yönetir; girdi bekleyen kanalları izler.

Path

Dosya ve dizin yollarını temsil eder (Java 7+, NIO.2).

NIO.2 (java.nio.file, Java 7+) dosya işlemleri için Path ve Files sınıflarını ekledi.

NIO

Akış mı, Kanal mı?

Klasik G/Ç: akışDosyaProgram→ byte byte →tek yön, sıralı, beklemeliNIO: kanal + tamponDosyaProgramkanaltamponçift yön, bloklar, beklemesiz olabilir
Klasik G/Ç (java.io)NIO (java.nio)
Temel kavramakış (stream)kanal (channel) + tampon (buffer)
Yöntek yön: girdi ya da çıktıkanal çift yönlü olabilir
Birimbyte / karaktertampon dolusu blok
Beklemeher zaman bloklayanbloklamayan (non-blocking) mod da var
Çok bağlantıbağlantı başına bir iş parçacığıtek iş parçacığı + seçici
Ne zaman?basit dosya/konsol işleriçok sayıda bağlantılı sunucular, büyük dosyalar

NIO

NIO Tamponları

  • Tampon: verileri geçici olarak saklayan, sabit boyutlu bir bellek alanıdır (bir dizi + konum bilgileri).
  • Her ilkel tür için bir sınıf: ByteBuffer, CharBuffer, ShortBuffer, IntBuffer, LongBuffer, FloatBuffer, DoubleBuffer.
  • Oluşturma: ByteBuffer.allocate(1024) veya var olan diziyi sarmak: ByteBuffer.wrap(dizi).
ÖzellikAnlamı
kapasite (capacity)tutulabilecek en fazla eleman; hiç değişmez
konum (position)bir sonraki okuma/yazmanın yapılacağı indeks
sınır (limit)okunabilecek/yazılabilecek ilk olmayan indeks
işaret (mark)mark() ile saklanan konum; reset() oraya döner
0M1E2R3H4A5B6A789position = 3limit = 7capacity = 10mark = 1okunacak: remaining() = limit − position = 4

Her zaman: 0 ≤ mark ≤ position ≤ limit ≤ capacity.

NIO

ByteBuffer Örneği

public class TamponOrnek {
  public static void main(String[] args) {
    // 1 KB boyutunda bir ByteBuffer oluştur
    ByteBuffer buffer = ByteBuffer.allocate(1024);
    // Veri yazma
    buffer.put("Merhaba, Java!".getBytes());
    System.out.println("put sonrası  : " + buffer);
    // Buffer'ı okuma moduna geçirme
    buffer.flip();
    System.out.println("flip sonrası : " + buffer);
    // Veriyi okuma
    while (buffer.hasRemaining()) {
      System.out.print((char) buffer.get());
    }
    System.out.println();
  }
}
Çıktı
put sonrası  : java.nio.HeapByteBuffer[pos=14 lim=1024 cap=1024]
flip sonrası : java.nio.HeapByteBuffer[pos=0 lim=14 cap=1024]
Merhaba, Java!
  • put yazar, konum 14'e ilerler.
  • flip(): sınır = 14, konum = 0: okuma modu.
  • hasRemaining(): konum < sınır mı?

NIO

Tampon ve Türkçe Karakterler

public class TamponUmlaut {
  public static void main(String[] args) {
    ByteBuffer b = ByteBuffer.allocate(64);
    b.put("Dünya".getBytes(StandardCharsets.UTF_8));
    b.flip();
    while (b.hasRemaining())
      System.out.print((char) b.get());      // yanlış
    System.out.println();
    b.rewind();
    System.out.println(StandardCharsets.UTF_8.decode(b));  // doğru
  }
}
Çıktı
Dᅢᄐnya
Dünya

(char) buffer.get() yalnızca ASCII metinde doğrudur. “Dünya” UTF-8'de 6 byte'tır (ü = C3 BC); byte'ları tek tek char'a çevirmek ü'yü bozar. Metni çözmek için Charset.decode kullanılmalıdır.

NIO

put, flip, get, compact, rewind, clear: Adım Adım

ByteBuffer (capacity = 8) · yazma modu01234567pos=0lim=8

Boş tampon: konum 0, sınır = kapasite = 8.

ByteBuffer (capacity = 8) · yazma moduJ01234567pos=1lim=8

put konuma yazar ve konumu 1 ilerletir.

ByteBuffer (capacity = 8) · yazma moduJ0A1234567pos=2lim=8

Konum 2.

ByteBuffer (capacity = 8) · yazma moduJ0A1V234567pos=3lim=8

Konum 3.

ByteBuffer (capacity = 8) · yazma moduJ0A1V2A34567pos=4lim=8

4 byte yazıldı; konum 4.

ByteBuffer (capacity = 8) · okuma moduJ0A1V2A34567pos=0lim=4

flip(): sınır = eski konum (4), konum = 0. Yalnızca yazılan 4 byte okunabilir.

ByteBuffer (capacity = 8) · okuma moduJ0A1V2A34567pos=1lim=4

get() konumdaki byte'ı döndürür, konum 1 olur.

ByteBuffer (capacity = 8) · okuma moduJ0A1V2A34567pos=2lim=4

c2 = 'A'; okunmamış 2 byte kaldı (V, A).

ByteBuffer (capacity = 8) · yazma moduV0A1V2A34567pos=2lim=8

compact(): okunmamış V, A başa kopyalanır; konum 2, sınır 8. 2–3'teki eski byte'lar silinmez, üzerine yazılacak.

ByteBuffer (capacity = 8) · yazma moduV0A1!2A34567pos=3lim=8

'!' konum 2'ye yazılır; eski 'V'nin üzerine.

ByteBuffer (capacity = 8) · okuma moduV0A1!2A34567pos=0lim=3

Okumaya geç: sınır 3 (V, A, !).

ByteBuffer (capacity = 8) · okuma moduV0A1!2A34567pos=1lim=3

Bir byte okunur ('V'), konum 1.

ByteBuffer (capacity = 8) · okuma moduV0A1!2A34567pos=0lim=3

rewind(): konum 0, sınır değişmez: aynı veri baştan yeniden okunabilir.

ByteBuffer (capacity = 8) · yazma moduV0A1!2A34567pos=0lim=8

clear(): konum 0, sınır 8. Veri silinmez; sadece “boş” sayılır, üzerine yazılır.

ByteBuffer (capacity = 8) · yazma moduV0A1!2A34567pos=0lim=8

toString() tamponun konum, sınır ve kapasitesini gösterir.

import java.nio.ByteBuffer;

public class TamponIzle {
  public static void main(String[] args) {
    ByteBuffer b = ByteBuffer.allocate(8);
    b.put((byte) 'J');
    b.put((byte) 'A');
    b.put((byte) 'V');
    b.put((byte) 'A');
    b.flip();
    char c1 = (char) b.get();
    char c2 = (char) b.get();
    b.compact();
    b.put((byte) '!');
    b.flip();
    b.get();
    b.rewind();
    b.clear();
    System.out.println(c1 + "" + c2 + " " + b);
  }
}

NIO

Tampon İşlemleri

MetotNe yapar?positionlimit
put(x)konuma veri yazar+1—
get()konumdaki veriyi okur+1—
flip()yazmadan okumaya geçer0eski position
rewind()aynı veriyi baştan yeniden okumak için0değişmez
clear()yeniden yazmaya hazırlar; veriyi silmez0capacity
compact()okunmamış veriyi başa taşır, yazmaya devam ederkalan sayısıcapacity
mark() / reset()konumu işaretler / işarete döner— / mark—

Avantajlar

  • Veri üzerinde esnek gezinme ve değiştirme
  • Verimli bellek yönetimi: tek alan tekrar tekrar kullanılır

Dezavantajlar

  • Kullanımı karmaşıktır (flip unutmak sık hata)
  • Boyutu sonradan değiştirilemez

NIO

Deneyin: ByteBuffer

 

Yeşil hücreler position ile limit arasıdır: yazma modunda boş yer, okuma modunda okunacak veri. Deneyin: üç kez put, flip, iki kez get, compact. Ya da flip etmeden get deyin.

NIO

NIO Kanalları (Channel)

  • Kanal, giriş ve çıkış verilerini yönetir: bir dosya, soket veya belirli bir protokol üzerinden veri alışverişi sağlar.
  • Veri her zaman bir tampon üzerinden okunur (read(tampon)) veya yazılır (write(tampon)).
Kanal türüKullanımı
FileChanneldosyada okuma ve yazma
SocketChannelTCP ağ soketi üzerinden veri iletimi
ServerSocketChannelgelen TCP bağlantılarını kabul etme
DatagramChannelUDP soketi üzerinden veri iletimi

NIO

Kanal Oluşturma ve Kapatma

public class KanalAcKapa {
  public static void main(String[] args) throws IOException {
    // FileChannel oluşturma
    FileChannel fileChannel = FileChannel.open(
        Paths.get("dosya.txt"), StandardOpenOption.READ);
    // SocketChannel oluşturma
    SocketChannel socketChannel = SocketChannel.open();
    socketChannel.connect(new InetSocketAddress("example.com", 80));
    System.out.println("Bağlandı: " + socketChannel.isConnected());
    // Kanal kapatma
    fileChannel.close();
    socketChannel.close();
  }
}
Çıktı
Bağlandı: true

FileChannel.open(yol, seçenekler…) dosya kanalı, SocketChannel.open() + connect ağ kanalı açar. Kanallar da close() ile (veya try-with-resources ile) kapatılmalıdır.

NIO

FileChannel ile Okuma

public class KanalOku {
  public static void main(String[] args) throws IOException {
    try (FileChannel channel = FileChannel.open(
        Paths.get("dosya.txt"), StandardOpenOption.READ)) {
      ByteBuffer buffer = ByteBuffer.allocate(1024);
      // Dosyadan veriyi okuma
      int bytesRead = channel.read(buffer);
      System.out.println(bytesRead + " byte okundu");
      // Buffer'ı okuma moduna geçirme
      buffer.flip();
      // Veriyi ekrana yazma
      while (buffer.hasRemaining()) {
        System.out.print((char) buffer.get());
      }
    }
  }
}
Çıktı
17 byte okundu
NIO kanal ornegi

channel.read(buffer) tampona okur, okunan byte sayısını (dosya sonunda −1) döndürür. Okumadan önce flip() unutulmamalı.

NIO

FileChannel ile Yazma

public class KanalYaz {
  public static void main(String[] args) throws IOException {
    try (FileChannel kanal = FileChannel.open(Paths.get("cikti.txt"),
        StandardOpenOption.CREATE, StandardOpenOption.WRITE)) {
      byte[] veri = "Kanal ile yazıldı\n".getBytes(StandardCharsets.UTF_8);
      ByteBuffer tampon = ByteBuffer.wrap(veri);
      int yazilan = kanal.write(tampon);
      System.out.println(yazilan + " byte yazıldı");
    }
  }
}
Çıktı
20 byte yazıldı

ByteBuffer.wrap(dizi) hazır bir diziyi tampon olarak kullanır (konum 0, sınır = dizi boyu); bu yüzden flip() gerekmez. CREATE: dosya yoksa oluştur.

NIO

SocketChannel Kullanımı

try (SocketChannel socketChannel = SocketChannel.open()) {
  socketChannel.connect(new InetSocketAddress("example.com", 80));
  ByteBuffer buffer = ByteBuffer.allocate(1024);
  buffer.put(("GET / HTTP/1.1\r\nHost: example.com\r\n"
      + "Connection: close\r\n\r\n").getBytes(StandardCharsets.US_ASCII));
  buffer.flip();
  // Veriyi sokete yazma
  socketChannel.write(buffer);
  // Cevabı okuma
  buffer.clear();
  socketChannel.read(buffer);
  buffer.flip();
  String cevap = StandardCharsets.US_ASCII.decode(buffer).toString();
  System.out.println(cevap.substring(0, cevap.indexOf("\r\n")));
} catch (IOException e) {
  e.printStackTrace();
}
Çıktı
HTTP/1.1 200 OK

main gövdesi. Ağ bağlantısı da bir kanaldır: aynı put / flip / write kalıbı. HTTP/1.1 isteğinde Host başlığı zorunludur.

NIO

NIO: Avantajlar ve Dezavantajlar

Avantajlar

  • Performans: bloklar hâlinde, klasik G/Ç'den verimli aktarım
  • Esneklik: tampon ve kanallarla esnek veri işleme; çift yönlü kanallar
  • Dosya ve ağ için aynı yöntem
  • Çoklu bağlantı: seçiciyle birden çok kanal tek iş parçacığında; yalnızca etkin kanallar işlenir

Dezavantajlar

  • Karmaşıklık: klasik G/Ç'ye göre daha zor
  • Tampon durumunu ve hata durumlarını (yarım okuma, kopan bağlantı) yönetmek gerekir
  • Seçici için öğrenme eğrisi: çok kanalın durumunu doğru izlemek gerekir
Kural: tek bir dosyayı satır satır okuyacaksanız BufferedReader veya Files yeterlidir. Kanallar, çok büyük dosyalar ve çok sayıda ağ bağlantısı için öne çıkar.

NIO

Path ve Files (NIO.2)

public class DosyalarNio {
  public static void main(String[] args) throws IOException {
    Path yol = Path.of("veri", "notlar.txt");      // Java 11+
    Files.createDirectories(yol.getParent());
    Files.writeString(yol, "Ali 70\nAyşe 85\nCan 90\n");
    System.out.println(yol.getFileName() + ": " + Files.exists(yol));
    System.out.println("Boyut: " + Files.size(yol) + " byte");
    List<String> satirlar = Files.readAllLines(yol);
    for (String satir : satirlar) {
      System.out.println(satir.toUpperCase());
    }
    Files.write(yol, List.of("Deniz 60"), StandardOpenOption.APPEND);
    System.out.println(Files.readAllLines(yol).size() + " satır");
  }
}
Çıktı
notlar.txt: true
Boyut: 23 byte
ALI 70
AYŞE 85
CAN 90
4 satır
MetotGörevi
Path.of(…) / Paths.get(…)yol oluşturur (File'ın modern karşılığı)
Files.readAllLines, Files.readStringtüm dosyayı tek çağrıda okur (UTF-8)
Files.write, Files.writeStringtek çağrıda yazar
Files.exists, size, copy, move, deletedosya sistemi işlemleri

NIO

NIO Seçici (Selector)

  • Birden çok kanalı tek bir iş parçacığında izleme mekanizmasıdır.
  • Selector.open() ile yeni bir seçici oluşturulur.
  • Kanal bloklamayan moda alınır ve seçiciye kaydedilir: channel.register(selector, ops).
  • Kanalın hangi olayları beklediği SelectionKey sabitleriyle belirlenir. select() en az bir kanal hazır olana kadar bekler.
iş parçacığıselect()SelectorKanal 1OP_ACCEPThazırKanal 2OP_READbekliyorKanal 3OP_READhazırKanal 4OP_WRITEbekliyor
SelectionKeyAnlamı
OP_READkanaldan okuma yapılabilir
OP_WRITEkanala yazma yapılabilir
OP_CONNECTbağlantı kurma tamamlandı
OP_ACCEPTbağlantı kabul edilebilir

NIO

Seçici ile Sunucu

public class SeciciGoster {
  public static void main(String[] args) throws IOException {
    ServerSocketChannel kanal = ServerSocketChannel.open();
    Selector secici = Selector.open();
    kanal.bind(new InetSocketAddress(8080));
    kanal.configureBlocking(false);
    kanal.register(secici, SelectionKey.OP_ACCEPT);
    while (true) {
      int hazirKanallar = secici.select();
      if (hazirKanallar > 0) {
        Set<SelectionKey> secilenler = secici.selectedKeys();
        for (SelectionKey secilen : secilenler) {
          if (secilen.isAcceptable()) {
            // Bağlantı kabul
          } else if (secilen.isReadable()) {
            // Veri okuma
          }
        }
        secilenler.clear(); // İşlenen anahtarları temizle
      }
    }
  }
}
  • Sunucu kanalı 8080 portuna bağlanır, bloklamayan moda alınır, OP_ACCEPT için kaydedilir.
  • select() hazır kanal sayısını, selectedKeys() hazır anahtarları verir.
  • İşlenen anahtarlar clear() ile temizlenmezse sonraki turda yine gelir.
  • Gerçek sunucuda kabul edilen istemci OP_READ ile kaydedilir ve veri okunur.

Özet

Özet

KonuAnahtar sınıf / metotHatırlanacak
AkışlarSystem.in, System.out, System.err, Scannerveri kaynaktan hedefe sırayla akar
Metin / ikiliASCII, ISO-8859-9, UTF-8; DataOutputStream127: metin 3 byte, int 4 byte; aynı kodlamayla oku-yaz
Dosya sistemiFile, Path, FilesFile yalnızca yoldur, içeriği okumaz
Metin okuma/yazmaScanner, BufferedReader, PrintWriter, FileWriterreadLine() sonda null; FileWriter(ad, true) ekler
Bayt akışlarıFileInputStream, ByteArray…, Piped…, Sequence…read() sonda −1; okunan int'te tutulur
TamponlamaBuffered…, flush()disk erişimini toplu yapar; kapatmayı unutma
Kopyalamaread(byte[]), write(b, 0, n), Files.copyyalnızca okunan n byte yazılır
Rasgele erişimRandomAccessFile, seekk. kayıt = k × kayıt boyu
SerileştirmeSerializable, ObjectOutputStream, transientstatic/transient yazılmaz
NIOByteBuffer, FileChannel, Selectoryazdıktan sonra flip(), tekrar yazmadan önce clear()/compact()

Özet

Sık Yapılan Hatalar (1)

Denetlenen istisnayı ele almamakerror: unreported exception FileNotFoundException; must be caught or declared to be thrown

new FileReader("a.txt") try-catch içinde olmalı ya da metot throws IOException demeli.

import unutmakerror: cannot find symbol symbol: class Scanner

import java.util.Scanner; (G/Ç sınıfları için import java.io.*;).

Windows yolunda tek ters bölüerror: illegal escape character new File("C:\dersler\notlar.txt")

\n, \d kaçış dizisi sayılır. "C:\\dersler\\notlar.txt" veya "C:/dersler/notlar.txt" yazın.

finally'de null kontrolü yokException in thread "main" java.lang.NullPointerException: Cannot invoke "java.util.Scanner.close()"

Dosya açılamayınca scanner null kalır. if (scanner != null) ya da try-with-resources kullanın.

Özet

Sık Yapılan Hatalar (2)

nextInt'ten sonra nextLineYaş: 19⏎ Ad: Ayşe⏎ → çıktı: [] 19

nextInt() satır sonunu tamponda bırakır; ardından gelen nextLine() boş satırı okur. Araya bir klavye.nextLine(); ekleyin.

Tamponlu yazıcıyı kapatmamakbw.write("kayit"); // close yok not.txt boyutu: 0

Veri tamponda kalır, dosya boş görünür. close() (veya flush()) çağırın; en iyisi try-with-resources.

flip() unutmakb.put('J'); b.put('A'); b.get() → 0 (konum 2'deki boş hücre)

Yazdıktan sonra okumadan önce flip() şarttır; yoksa yazılan veri değil, sonrası okunur.

Serializable unutmakjava.io.NotSerializableException: Araba

writeObject edilen sınıf (ve alanlarının türleri) implements Serializable olmalı.

read()'i byte'ta tutmakbyte b; while ((b = (byte) in.read()) != -1)

255 değerli byte (−1'e dönüşür) dosya sonu sanılır. int kullanın.

write(parca) yazmakout.write(parca); // son blokta eski veri de gider

Son blokta dizi tam dolmaz; write(parca, 0, boyut) yazın.

Özet

Örnek Problemler (1)

try (PrintWriter pw = new PrintWriter("s.txt")) {
  pw.print(12);
  pw.print(' ');
  pw.print(345);
}
System.out.println(new File("s.txt").length());
try (DataOutputStream d = new DataOutputStream(new FileOutputStream("s.dat"))) {
  d.writeInt(12);
  d.writeInt(345);
}
System.out.println(new File("s.dat").length());
  1. Bu kod ne yazdırır? 12 ve 345 önce metin, sonra ikili olarak yazılıyor.
Cevabı göster
  1. 6 ve 8. Metin: "12 345" = 6 karakter = 6 byte. İkili: iki int = 2 × 4 = 8 byte.

Özet

Örnek Problemler (2)

try (FileWriter w = new FileWriter("a.txt")) {
  w.write("ab");
}
try (FileWriter w = new FileWriter("a.txt")) {
  w.write("cd");
}
try (FileWriter w = new FileWriter("a.txt", true)) {
  w.write("ef");
}
try (BufferedReader r = new BufferedReader(new FileReader("a.txt"))) {
  System.out.println(r.readLine());
}
  1. Bu kod ne yazdırır? a.txt başta yok.
Cevabı göster
  1. cdef. İkinci FileWriter dosyayı sıfırlar (“ab” silinir), üçüncüsü true ile sona ekler.

Özet

Örnek Problemler (3)

ByteBuffer b = ByteBuffer.allocate(10);
b.put((byte) 1).put((byte) 2).put((byte) 3);
b.flip();
b.get();
System.out.println(b.position() + " " + b.limit() + " " + b.remaining());
b.compact();
System.out.println(b.position() + " " + b.limit());
b.clear();
System.out.println(b.position() + " " + b.limit() + " " + b.get(0));
  1. Bu kod üç satırda ne yazdırır?
Cevabı göster
  1. 1 3 2 / 2 10 / 0 10 2. flip: konum 0, sınır 3; bir get → konum 1, kalan 2. compact: 2 ve 3 başa taşınır, konum 2, sınır 10. clear: konum 0, sınır 10; veri silinmez, get(0) = 2.

Özet

Örnek Problemler (4)

try (RandomAccessFile f = new RandomAccessFile("r.dat", "rw")) {
  for (int i = 1; i <= 4; i++) f.writeInt(i * 10);
  f.seek(4);
  int a = f.readInt();
  f.seek(f.length() - 4);
  int b = f.readInt();
  System.out.println(a + " " + b + " " + f.getFilePointer());
}
  1. Bu kod ne yazdırır?
Cevabı göster
  1. 20 40 16. Kayıtlar 10, 20, 30, 40 (her biri 4 byte). seek(4) → 20. length() - 4 = 12 → 40. Okumadan sonra işaretçi 16, yani dosya sonu.

Özet

Alıştırmalar (1)

  1. Sayaç: metin.txt dosyasının satır, kelime ve karakter sayısını (satır sonları hariç) bulan bir program yazın. Dosya: “Java ile dosya / okuma ve yazma / (boş satır) / çok kolay”.
Çözüm
int satirSayisi = 0, kelimeSayisi = 0, karakterSayisi = 0;
try (BufferedReader br = new BufferedReader(new FileReader("metin.txt"))) {
  String satir;
  while ((satir = br.readLine()) != null) {
    satirSayisi++;
    karakterSayisi += satir.length();
    String temiz = satir.trim();
    if (!temiz.isEmpty()) kelimeSayisi += temiz.split("\\s+").length;
  }
} catch (IOException e) {
  System.err.println("Hata: " + e.getMessage());
}
System.out.println(satirSayisi + " satır, " + kelimeSayisi + " kelime, "
    + karakterSayisi + " karakter");
Çıktı
4 satır, 8 kelime, 37 karakter

Özet

Alıştırmalar (2)

  1. Kareler: klavyeden n okuyun; 1..n sayılarını ve karelerini kareler.txt'ye yazın, sonra dosyayı okuyup kareler toplamını bulun.
Çözüm
Scanner klavye = new Scanner(System.in);
try (PrintWriter pw = new PrintWriter(new FileWriter("kareler.txt"))) {
  int n = klavye.nextInt();
  for (int i = 1; i <= n; i++) {
    pw.println(i + " " + i * i);
  }
}
int toplam = 0;
try (Scanner dosya = new Scanner(new File("kareler.txt"))) {
  while (dosya.hasNextInt()) {
    dosya.nextInt();               // i
    toplam += dosya.nextInt();     // i * i
  }
}
System.out.println("Kareler toplamı: " + toplam);
Çıktı (n = 5)
Kareler toplamı: 55

Özet

Alıştırmalar (3)

  1. Dizi saklama: int[] dizi = {3, 1, 4, 1, 5} dizisini dizi.ser'e serileştirin, geri okuyup eleman sayısını ve toplamını yazdırın (diziler serileştirilebilir).
Çözüm
int[] dizi = {3, 1, 4, 1, 5};
try (ObjectOutputStream c = new ObjectOutputStream(new FileOutputStream("dizi.ser"))) {
  c.writeObject(dizi);
}
try (ObjectInputStream g = new ObjectInputStream(new FileInputStream("dizi.ser"))) {
  int[] okunan = (int[]) g.readObject();
  int toplam = 0;
  for (int x : okunan) toplam += x;
  System.out.println(okunan.length + " eleman, toplam " + toplam);
}
Çıktı
5 eleman, toplam 14

Özet

Alıştırmalar (4)

  1. Not dosyası: 5 notu RandomAccessFile ile int olarak yazın. Verilen sıra numarasındaki notu dosyanın tamamını okumadan bulun. Hangi konuma seek edilmeli?
  2. Düşünün: 10 000 satırlık bir kayıt dosyasının yalnızca son kaydını okumak için sıralı akış mı, rasgele erişim mi uygundur? Satır boyları farklıysa ne değişir?
Cevaplar / ipuçları
  1. f.seek((sira - 1) * 4); int not = f.readInt(); 3. notun konumu 8.
  2. Sabit boylu kayıtlarda rasgele erişim: doğrudan seek((n - 1) * boy). Satır boyları farklıysa konum hesaplanamaz: baştan sıralı okunur ya da sondan geriye \n aranır.

Özet

Kaynaklar

  • Oracle, The Java Tutorials: “Basic I/O” (I/O Streams, File I/O featuring NIO.2), “Scanning and Formatting”.
  • Oracle, Java SE 21 API Documentation: java.io, java.nio, java.nio.channels, java.nio.file paketleri.
  • Oracle, Java Object Serialization Specification.
  • J. Gosling ve diğ., The Java Language Specification, Java SE 21 Edition (3.10.6 kaçış dizileri, 14.20.3 try-with-resources).
  • P. Deitel ve H. Deitel, Java: How to Program, Early Objects, 11. baskı, Pearson, 2017 (Bölüm 15: Files, Input/Output Streams, NIO and XML Serialization).
  • C. S. Horstmann, Core Java, Volume II: Advanced Features, 12. baskı, 2022 (Bölüm 2: Input and Output).
  • K. Sierra ve B. Bates, Head First Java, 3. baskı, O'Reilly, 2022 (Bölüm 16: Saving Objects (and Text)).
  • A. B. Downey ve C. Mayfield, Think Java, 2. baskı, O'Reilly, 2019.

Son

Bölüm 7: Giriş Çıkış
Sonraki bölüm: Bölüm 8: Grafikler
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.