Dosya İşlemleri (NIO.2)
Bir önceki derste Java'nın eski I/O sistemini gördük. Şimdi modern hali: NIO.2 (New I/O 2). Java 7 ile gelen bu API, dosya işlemlerini çok daha kolay ve güçlü hale getiriyor. Eski File sınıfı yerine Path ve Files sınıflarını kullanacağız.
Neden NIO.2?
Eski java.io.File sınıfı birçok soruna sahipti:
Hata durumunda exception fırlatmaz,
falsedöner — hatanın sebebini bilemezsinSembolik link desteği yok
Dosya izinleri ile çalışmak zor
Büyük dizin listeleme performansı kötü
Platform bağımlı davranışlar
NIO.2 bunları çözüyor. Adresler: java.nio.file.Path ve java.nio.file.Files.
Analoji: File eski bir el aleti, Path + Files ise elektrikli alet seti. İkisiyle de iş yapabilirsin ama yenisi daha hızlı, daha güvenli, daha yetenekli.
Path — Dosya Yolunu Temsil Et
Path, dosya sistemindeki bir yolu temsil eder. Dosyanın var olması gerekmez — sadece yolu tutar.
// Path oluşturma
Path p1 = Path.of("dosya.txt");
Path p2 = Path.of("/home", "kullanici", "belgeler", "rapor.txt");
Path p3 = Path.of("C:\\Users\\Ali\\Desktop\\proje");
Path p4 = Paths.get("dosya.txt"); // Eski yol, aynı şeyPath Bilgileri
Path p = Path.of("/home/ali/projeler/Main.java");
System.out.println(p.getFileName()); // Main.java
System.out.println(p.getParent()); // /home/ali/projeler
System.out.println(p.getRoot()); // /
System.out.println(p.getNameCount()); // 4 (home, ali, projeler, Main.java)
System.out.println(p.getName(0)); // home
System.out.println(p.getName(2)); // projeler
System.out.println(p.isAbsolute()); // truePath Birleştirme ve Çözme
Path base = Path.of("/home/ali");
Path dosya = Path.of("belgeler/rapor.txt");
// resolve — birleştir
Path tam = base.resolve(dosya);
System.out.println(tam); // /home/ali/belgeler/rapor.txt
// resolve ile string
Path tam2 = base.resolve("projeler/Main.java");
System.out.println(tam2); // /home/ali/projeler/Main.java
// relativize — göreceli yol bul
Path p1 = Path.of("/home/ali/belgeler");
Path p2 = Path.of("/home/ali/projeler/java");
System.out.println(p1.relativize(p2)); // ../../projeler/java
// normalize — gereksiz . ve .. temizle
Path karmasik = Path.of("/home/ali/../ali/./belgeler/rapor.txt");
System.out.println(karmasik.normalize()); // /home/ali/belgeler/rapor.txt
// toAbsolutePath — mutlak yola çevir
Path goreceli = Path.of("dosya.txt");
System.out.println(goreceli.toAbsolutePath()); // /mevcut/dizin/dosya.txtFiles — Dosya İşlemleri
Files sınıfı, dosya ve dizin işlemleri için statik metodlar sunar. Path ile birlikte kullanılır.
Dosya/Dizin Kontrolleri
Path p = Path.of("dosya.txt");
System.out.println(Files.exists(p)); // Var mı?
System.out.println(Files.notExists(p)); // Yok mu?
System.out.println(Files.isRegularFile(p)); // Normal dosya mı?
System.out.println(Files.isDirectory(p)); // Dizin mi?
System.out.println(Files.isReadable(p)); // Okunabilir mi?
System.out.println(Files.isWritable(p)); // Yazılabilir mi?
System.out.println(Files.size(p)); // Boyut (byte)Dosya Okuma
Tüm Satırları Oku
// Küçük dosyalar için — tüm satırları listeye oku
List<String> satirlar = Files.readAllLines(
Path.of("metin.txt"),
StandardCharsets.UTF_8
);
for (String satir : satirlar) {
System.out.println(satir);
}Tüm İçeriği String Olarak Oku
// Java 11+ — tek seferde string olarak oku
String icerik = Files.readString(Path.of("metin.txt"), StandardCharsets.UTF_8);
System.out.println(icerik);Tüm Byte'ları Oku
// İkili dosyalar için
byte[] bytes = Files.readAllBytes(Path.of("resim.png"));
System.out.println("Boyut: " + bytes.length + " byte");Stream ile Satır Satır Oku (Büyük Dosyalar)
// Büyük dosyalar için — lazy, bellekte tüm dosyayı tutmaz
try (Stream<String> satirlar = Files.lines(Path.of("buyuk.txt"), StandardCharsets.UTF_8)) {
satirlar
.filter(s -> !s.isBlank())
.map(String::trim)
.forEach(System.out::println);
}⚠️ `readAllLines` küçük dosyalar için, `lines` büyük dosyalar için!
readAllLinestüm dosyayı belleğe yükler. 1GB'lık bir dosyada OutOfMemoryError alırsın.linesise lazy — satır satır okur.
Dosya Yazma
String Yazma
// Java 11+ — string yaz
Files.writeString(
Path.of("cikti.txt"),
"Merhaba Dünya!\nJava NIO.2 ile yazıldı.",
StandardCharsets.UTF_8
);Satır Listesi Yazma
List<String> satirlar = List.of(
"Birinci satır",
"İkinci satır",
"Üçüncü satır"
);
Files.write(Path.of("cikti.txt"), satirlar, StandardCharsets.UTF_8);Dosyaya Ekleme (Append)
// Mevcut dosyanın sonuna ekle
Files.writeString(
Path.of("log.txt"),
"Yeni log satırı\n",
StandardCharsets.UTF_8,
StandardOpenOption.APPEND,
StandardOpenOption.CREATE // Yoksa oluştur
);Byte Yazma
byte[] veri = {72, 101, 108, 108, 111};
Files.write(Path.of("veri.bin"), veri);Dosya Oluşturma, Kopyalama, Taşıma, Silme
Dosya Oluşturma
// Boş dosya oluştur
Path yeniDosya = Files.createFile(Path.of("yeni.txt"));
// Geçici dosya oluştur
Path gecici = Files.createTempFile("prefix_", ".tmp");
System.out.println(gecici); // /tmp/prefix_123456.tmpDizin Oluşturma
// Tek dizin oluştur (üst dizin var olmalı)
Files.createDirectory(Path.of("yeni_dizin"));
// İç içe dizinler oluştur (mkdir -p gibi)
Files.createDirectories(Path.of("a/b/c/d"));Dosya Kopyalama
// Basit kopyalama
Files.copy(
Path.of("kaynak.txt"),
Path.of("hedef.txt")
);
// Üzerine yazarak kopyala
Files.copy(
Path.of("kaynak.txt"),
Path.of("hedef.txt"),
StandardCopyOption.REPLACE_EXISTING
);
// Dosya özelliklerini de kopyala
Files.copy(
Path.of("kaynak.txt"),
Path.of("hedef.txt"),
StandardCopyOption.REPLACE_EXISTING,
StandardCopyOption.COPY_ATTRIBUTES
);Dosya Taşıma / Yeniden Adlandırma
// Taşı
Files.move(
Path.of("eski_konum/dosya.txt"),
Path.of("yeni_konum/dosya.txt"),
StandardCopyOption.REPLACE_EXISTING
);
// Yeniden adlandır (aynı dizinde taşı)
Files.move(
Path.of("eski_isim.txt"),
Path.of("yeni_isim.txt")
);Dosya Silme
// Sil (yoksa NoSuchFileException fırlatır)
Files.delete(Path.of("silinecek.txt"));
// Sil (yoksa exception fırlatmaz)
boolean silindi = Files.deleteIfExists(Path.of("silinecek.txt"));
System.out.println("Silindi: " + silindi);⚠️ `Files.delete` ile dizin silmek istiyorsan dizin BOŞ olmalı! Dolu dizini silmek için walkFileTree kullanman gerekir (aşağıda göreceğiz).
Dizin İçeriğini Listeleme
list — Bir Seviye
try (Stream<Path> dosyalar = Files.list(Path.of("."))) {
dosyalar
.filter(Files::isRegularFile)
.forEach(System.out::println);
}walk — Recursive (Alt Dizinler Dahil)
// Tüm alt dizinlerdeki .java dosyalarını bul
try (Stream<Path> dosyalar = Files.walk(Path.of("src"))) {
List<Path> javaDosyalari = dosyalar
.filter(p -> p.toString().endsWith(".java"))
.toList();
javaDosyalari.forEach(System.out::println);
System.out.println("Toplam: " + javaDosyalari.size() + " dosya");
}walk ile Derinlik Sınırlama
// Maksimum 2 seviye derinlik
try (Stream<Path> dosyalar = Files.walk(Path.of("proje"), 2)) {
dosyalar.forEach(System.out::println);
}find — Koşullu Arama
// 1MB'dan büyük dosyaları bul
try (Stream<Path> sonuclar = Files.find(
Path.of("."),
Integer.MAX_VALUE, // Sınırsız derinlik
(path, attr) -> attr.isRegularFile() && attr.size() > 1_000_000
)) {
sonuclar.forEach(p -> {
try {
System.out.printf("%s (%d KB)%n", p, Files.size(p) / 1024);
} catch (IOException e) {
e.printStackTrace();
}
});
}walkFileTree — Gelişmiş Dizin Gezme
walk basit durumlar için yeterli. Ama dizin silme, kopyalama gibi karmaşık işlemlerde walkFileTree lazım.
Dolu Dizin Silme
public static void dizinSil(Path dizin) throws IOException {
Files.walkFileTree(dizin, new SimpleFileVisitor<>() {
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
Files.delete(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc)
throws IOException {
Files.delete(dir);
return FileVisitResult.CONTINUE;
}
});
}// Kullanım
dizinSil(Path.of("silinecek_dizin"));SimpleFileVisitor dört metod sağlar:
preVisitDirectory— dizine girmeden öncevisitFile— her dosya içinvisitFileFailed— dosyaya erişilemezsepostVisitDirectory— dizinden çıkarken
Dizin Kopyalama
public static void dizinKopyala(Path kaynak, Path hedef) throws IOException {
Files.walkFileTree(kaynak, new SimpleFileVisitor<>() {
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs)
throws IOException {
Path hedefDir = hedef.resolve(kaynak.relativize(dir));
Files.createDirectories(hedefDir);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs)
throws IOException {
Path hedefFile = hedef.resolve(kaynak.relativize(file));
Files.copy(file, hedefFile, StandardCopyOption.REPLACE_EXISTING);
return FileVisitResult.CONTINUE;
}
});
}Dosya Özellikleri
Path p = Path.of("dosya.txt");
// Temel özellikler
BasicFileAttributes attr = Files.readAttributes(p, BasicFileAttributes.class);
System.out.println("Boyut: " + attr.size());
System.out.println("Oluşturulma: " + attr.creationTime());
System.out.println("Son değişiklik: " + attr.lastModifiedTime());
System.out.println("Son erişim: " + attr.lastAccessTime());
System.out.println("Dizin mi: " + attr.isDirectory());
System.out.println("Sembolik link mi: " + attr.isSymbolicLink());Son Değişiklik Zamanını Ayarla
Files.setLastModifiedTime(
Path.of("dosya.txt"),
FileTime.from(Instant.now())
);Gerçek Hayat Örnekleri
Log Dosyası Analizi
public static Map<String, Long> logSeviyeSay(Path logDosya) throws IOException {
try (Stream<String> satirlar = Files.lines(logDosya)) {
return satirlar
.filter(s -> !s.isBlank())
.map(s -> s.split("\\s+")[0]) // İlk kelime = seviye
.collect(Collectors.groupingBy(
Function.identity(),
Collectors.counting()
));
}
}
// Kullanım
Map<String, Long> sonuc = logSeviyeSay(Path.of("app.log"));
System.out.println(sonuc); // {INFO=150, WARN=23, ERROR=5}CSV Dosyası Okuma
public record Ogrenci(String isim, int yas, double not) {}
public static List<Ogrenci> csvOku(Path dosya) throws IOException {
try (Stream<String> satirlar = Files.lines(dosya, StandardCharsets.UTF_8)) {
return satirlar
.skip(1) // Başlık satırını atla
.map(s -> s.split(","))
.map(p -> new Ogrenci(p[0].trim(), Integer.parseInt(p[1].trim()),
Double.parseDouble(p[2].trim())))
.toList();
}
}Dosya Uzantısına Göre Gruplama
public static Map<String, List<Path>> uzantiyaGoreGrupla(Path dizin) throws IOException {
try (Stream<Path> dosyalar = Files.walk(dizin)) {
return dosyalar
.filter(Files::isRegularFile)
.collect(Collectors.groupingBy(p -> {
String isim = p.getFileName().toString();
int son = isim.lastIndexOf('.');
return son > 0 ? isim.substring(son) : "uzantısız";
}));
}
}
// Kullanım
Map<String, List<Path>> gruplar = uzantiyaGoreGrupla(Path.of("proje"));
gruplar.forEach((uzanti, dosyalar) ->
System.out.println(uzanti + ": " + dosyalar.size() + " dosya"));Eski API vs NIO.2 Karşılaştırma
| İşlem | Eski (File) | Yeni (NIO.2) |
|---|---|---|
| Yol oluştur | new File("a.txt") | Path.of("a.txt") |
| Var mı? | file.exists() | Files.exists(path) |
| Oku | BufferedReader + FileReader | Files.readAllLines(path) |
| Yaz | BufferedWriter + FileWriter | Files.writeString(path, str) |
| Kopyala | Manuel stream kopyalama | Files.copy(src, dst) |
| Sil | file.delete() (boolean döner) | Files.delete(path) (exception fırlatır) |
| Listele | file.listFiles() (null dönebilir) | Files.list(path) (stream döner) |
💡 Yeni projlerde her zaman NIO.2 kullan. Eski
FileAPI'si sadece legacy kodda karşına çıkar. Dönüşüm:file.toPath()vepath.toFile()ile geçiş yapabilirsin.
Özet
Path dosya yolunu temsil eder —
resolve,relativize,normalizeile yol işlemleri yapılırFiles sınıfı tüm dosya operasyonlarını statik metodlarla sunar
`readAllLines` küçük dosyalar, `Files.lines` büyük dosyalar için kullanılır
`Files.copy`, `Files.move`, `Files.delete` ile kopyalama/taşıma/silme yapılır
`Files.walk` recursive dizin gezme, `walkFileTree` gelişmiş visitor pattern ile gezme sağlar
NIO.2, eski
FileAPI'sinin yerini almıştır — yeni projelerde daima NIO.2 tercih et
AI Asistan
Sorularını yanıtlamaya hazır