1. Powtórka: File vs Path vs Files
Stary, dobry File
Tak… w projektach Java nie brakuje kodu legacy. Dlatego przypomnijmy sobie weterana Javy, klasę File. Pojawiła się w języku już w pierwszej wersji i pozwala pracować z plikami i katalogami: sprawdzać, czy plik istnieje, jego nazwę, rozmiar, datę modyfikacji, tworzyć i usuwać pliki oraz katalogi. Jak już wiemy, ma sporo wad: część metod działa nieintuicyjnie, nie wszystkie operacje są wspierane wieloplatformowo, a niektóre metody zwracają nie wyjątki, lecz po prostu false (co nie zawsze jest wygodne przy debugowaniu).
import java.io.File;
File file = new File("example.txt");
System.out.println("Czy plik istnieje? " + file.exists());
System.out.println("Czy to plik? " + file.isFile());
System.out.println("Czy to katalog? " + file.isDirectory());
Path i Files: nowoczesne podejście (Java 7+)
Wraz z wydaniem Java 7 pojawił się pakiet java.nio.file, który oferuje potężniejsze i bardziej elastyczne narzędzia. Dwóch kluczowych bohaterów:
- Path — interfejs opisujący ścieżkę do pliku lub katalogu. Można powiedzieć, że to „łańcuch z inteligencją”: potrafi rozkładać, łączyć, analizować i porównywać ścieżki.
- Files — klasa narzędziowa z masą statycznych metod do pracy z plikami i katalogami: tworzenie, usuwanie, kopiowanie, odczyt, zapis, pobieranie informacji.
import java.nio.file.Path;
import java.nio.file.Paths;
Path path = Paths.get("example.txt");
System.out.println("Ścieżka: " + path);
Ciekawostka: klasa Path — to nie tylko łańcuch! Zna separatory katalogów, rozumie ścieżki względne i bezwzględne oraz działa tak samo dobrze na Windows i Linuksie.
Tabela porównawcza: File vs Path/Files
| Kryterium | File (stary API) | Path + Files (nowoczesny API) |
|---|---|---|
| Składnia | Styl obiektowy | Kombinacja Path + metody statyczne Files |
| Wieloplatformowość | Częściowa | Świetna, uwzględnia system operacyjny |
| Wyjątki | Nie zawsze | Prawie zawsze |
| Nowe możliwości | Nie | Tak (atrybuty, uprawnienia, linki itp.) |
| Rekomendowane | Raczej nie | Tak |
2. Pobieranie informacji o pliku/katalogu
Zobaczmy, jak sprawdzić najważniejsze rzeczy o pliku lub katalogu: czy obiekt istnieje, typ (plik czy katalog), rozmiar, nazwa, ścieżka i katalog nadrzędny.
Sprawdzanie istnienia
Za pomocą File
File file = new File("example.txt");
if (file.exists()) {
System.out.println("Plik istnieje!");
} else {
System.out.println("Nie znaleziono pliku.");
}
Za pomocą Path i Files
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Path path = Paths.get("example.txt");
if (Files.exists(path)) {
System.out.println("Plik istnieje!");
} else {
System.out.println("Nie znaleziono pliku.");
}
Wskazówka: w nowych projektach zawsze używaj Path i Files. To bezpieczniejsze, wygodniejsze i wieloplatformowe.
Sprawdzanie typu: plik czy katalog
File
if (file.isFile()) {
System.out.println("To plik.");
} else if (file.isDirectory()) {
System.out.println("To katalog.");
}
Path + Files
if (Files.isRegularFile(path)) {
System.out.println("To plik.");
} else if (Files.isDirectory(path)) {
System.out.println("To katalog.");
}
Pobieranie rozmiaru pliku
File
long size = file.length();
System.out.println("Rozmiar pliku: " + size + " bajtów");
Path + Files
try {
long size = Files.size(path);
System.out.println("Rozmiar pliku: " + size + " bajtów");
} catch (IOException e) {
System.out.println("Błąd podczas pobierania rozmiaru pliku: " + e.getMessage());
}
Uwaga: dla katalogu metoda length() zawsze zwróci 0, a Files.size(path) zgłosi IOException. Rozmiar katalogu to suma rozmiarów wszystkich plików w środku (więcej — w kolejnych wykładach).
Pobieranie ścieżki bezwzględnej i względnej
File
System.out.println("Ścieżka względna: " + file.getPath());
System.out.println("Ścieżka bezwzględna: " + file.getAbsolutePath());
Path
System.out.println("Ścieżka względna: " + path);
System.out.println("Ścieżka bezwzględna: " + path.toAbsolutePath());
Pobieranie nazwy pliku i katalogu nadrzędnego
File
System.out.println("Nazwa pliku: " + file.getName());
System.out.println("Katalog nadrzędny: " + file.getParent());
Path
System.out.println("Nazwa pliku: " + path.getFileName());
System.out.println("Katalog nadrzędny: " + path.getParent());
Pro tip: Path potrafi pracować nie tylko z plikami, ale też z katalogami, ścieżkami względnymi i bezwzględnymi, a także łatwo rozkłada ścieżkę na części.
3. Pobieranie atrybutów pliku
Czasem chcemy wiedzieć nie tylko rozmiar i nazwę pliku, lecz także coś ciekawszego: datę utworzenia, datę ostatniej modyfikacji, uprawnienia dostępu.
Data utworzenia i ostatniej modyfikacji
File (ograniczone)
long lastModified = file.lastModified();
System.out.println("Data ostatniej modyfikacji: " + new java.util.Date(lastModified));
Minus: uzyskanie daty utworzenia pliku przez File jest niemożliwe!
Path + Files
import java.nio.file.attribute.BasicFileAttributes;
import java.nio.file.attribute.FileTime;
try {
BasicFileAttributes attrs = Files.readAttributes(path, BasicFileAttributes.class);
FileTime creationTime = attrs.creationTime();
FileTime lastModifiedTime = attrs.lastModifiedTime();
System.out.println("Data utworzenia: " + creationTime);
System.out.println("Data ostatniej modyfikacji: " + lastModifiedTime);
} catch (IOException e) {
System.out.println("Błąd podczas pobierania atrybutów: " + e.getMessage());
}
Sprawdzanie uprawnień dostępu
File
System.out.println("Czy można czytać? " + file.canRead());
System.out.println("Czy można zapisywać? " + file.canWrite());
System.out.println("Czy można wykonywać? " + file.canExecute());
Path + Files
System.out.println("Czy można czytać? " + Files.isReadable(path));
System.out.println("Czy można zapisywać? " + Files.isWritable(path));
System.out.println("Czy można wykonywać? " + Files.isExecutable(path));
Ciekawostka: na Windows uprawnienia dostępu są ograniczone, a w systemach Unix można uzyskać i zmieniać znacznie więcej (np. przez atrybuty POSIX).
4. Typowe błędy przy pobieraniu informacji o plikach
Błąd nr 1: używanie File, gdy potrzebny jest Path.
Wielu początkujących nadal używa File, bo tak jest prościej albo „tak jest wszędzie w internecie”. Tymczasem za pomocą Path i Files można zrobić to samo, ale nowocześniej i bardziej niezawodnie.
Błąd nr 2: sprawdzanie typu obiektu tylko przez File.
Jeśli używasz wyłącznie file.isFile() i file.isDirectory(), możesz przegapić „specjalne” pliki (linki symboliczne, urządzenia). We współczesnym API są Files.isRegularFile(path), Files.isDirectory(path) i inne sprawdzenia.
Błąd nr 3: ignorowanie wyjątków.
Metody Files.size(path) i Files.readAttributes(...) zgłaszają IOException. Jeśli nie obsłużysz wyjątków, program zakończy działanie z błędem przy pierwszym problemie z dostępem albo gdy plik zniknie w trakcie pracy.
Błąd nr 4: oczekiwanie, że rozmiar katalogu można poznać bezpośrednio.
Metoda Files.size(path) dla katalogu zgłosi wyjątek. Aby poznać rozmiar katalogu, trzeba rekurencyjnie zsumować rozmiary wszystkich plików w środku.
Błąd nr 5: niewystarczające sprawdzanie uprawnień dostępu.
Nawet jeśli plik istnieje, możesz nie mieć uprawnień do jego odczytu lub zapisu. Zawsze sprawdzaj uprawnienia przed operacjami: Files.isReadable(path), Files.isWritable(path), Files.isExecutable(path).
GO TO FULL VERSION