Wejście/wyjście w Javie
Java oferuje dwa główne interfejsy API dla operacji wejścia/wyjścia: pakiet java.io
(oparty na strumieniu) e java.nio (Nowe wejścia/wyjścia, oparte na buforze).
W tym artykule omówimy oba podejścia.
Czego się nauczysz
- Strumień bajtów i znaków
- Odczyt i zapis plików
- BufferedReader i BufferedWriter
- NIO.2: Ścieżki i pliki
- Serializacja obiektu
- Najlepsze praktyki dotyczące operacji we/wy
Klasyczne we/wy: java.io
Hierarchia strumienia
| Typ | Strumień bajtów | Strumień postaci |
|---|---|---|
| Wejście | Strumień wejściowy | Czytelnik |
| Wyjścia | Strumień wyjściowy | Pisarz |
| Plik | Strumień pliku wejściowego/Stream pliku wyjściowego | FileReader/FileWriter |
| Buforowane | Buforowany strumień wejściowy/buforowany strumień wyjściowy | Buforowany czytnik/buforowany zapis |
Czytanie plików tekstowych
import java.io.*;
public class LettoreFileStudenti {
public static void leggiElencoStudenti(String percorso) {
// try-with-resources per chiusura automatica
try (BufferedReader reader = new BufferedReader(new FileReader(percorso))) {
String linea;
int numeroLinea = 1;
while ((linea = reader.readLine()) != null) {
System.out.println(numeroLinea + ": " + linea);
numeroLinea++;
}
} catch (FileNotFoundException e) {
System.err.println("File non trovato: " + percorso);
} catch (IOException e) {
System.err.println("Errore lettura: " + e.getMessage());
}
}
public static void main(String[] args) {
leggiElencoStudenti("studenti.txt");
}
}
Zapisywanie plików tekstowych
import java.io.*;
import java.util.*;
public class ScrittoreFileVoti {
public static void salvaVoti(String percorso, Map<String, Integer> voti) {
try (BufferedWriter writer = new BufferedWriter(new FileWriter(percorso))) {
writer.write("=== REGISTRO VOTI ===");
writer.newLine();
writer.newLine();
for (Map.Entry<String, Integer> entry : voti.entrySet()) {
String riga = entry.getKey() + ": " + entry.getValue();
writer.write(riga);
writer.newLine();
}
writer.newLine();
writer.write("Generato automaticamente");
System.out.println("File salvato: " + percorso);
} catch (IOException e) {
System.err.println("Errore scrittura: " + e.getMessage());
}
}
// Append mode: aggiunge al file esistente
public static void aggiungiVoto(String percorso, String studente, int voto) {
try (BufferedWriter writer = new BufferedWriter(
new FileWriter(percorso, true))) { // true = append
writer.write(studente + ": " + voto);
writer.newLine();
} catch (IOException e) {
System.err.println("Errore: " + e.getMessage());
}
}
public static void main(String[] args) {
Map<String, Integer> voti = new LinkedHashMap<>();
voti.put("Mario Rossi", 28);
voti.put("Laura Bianchi", 30);
voti.put("Giuseppe Verdi", 25);
salvaVoti("voti.txt", voti);
}
}
Strumień bajtów
import java.io.*;
public class CopiatoreFile {
public static void copiaFile(String sorgente, String destinazione) {
try (
FileInputStream fis = new FileInputStream(sorgente);
FileOutputStream fos = new FileOutputStream(destinazione);
BufferedInputStream bis = new BufferedInputStream(fis);
BufferedOutputStream bos = new BufferedOutputStream(fos)
) {
byte[] buffer = new byte[8192]; // 8KB buffer
int byteLetti;
while ((byteLetti = bis.read(buffer)) != -1) {
bos.write(buffer, 0, byteLetti);
}
System.out.println("File copiato con successo");
} catch (IOException e) {
System.err.println("Errore copia: " + e.getMessage());
}
}
// Lettura completa in byte array
public static byte[] leggiTuttiBytes(String percorso) throws IOException {
try (FileInputStream fis = new FileInputStream(percorso)) {
return fis.readAllBytes(); // Java 9+
}
}
public static void main(String[] args) {
copiaFile("originale.pdf", "copia.pdf");
}
}
NIO.2: Nowoczesne API
Wprowadzony w Javie 7, NIO.2 zapewnia bardziej nowoczesny i wydajny interfejs API do operacji na plikach i katalogach.
Ścieżki i ścieżki
import java.nio.file.*;
public class GestorePercorsi {
public static void main(String[] args) {
// Creare Path
Path path1 = Path.of("documenti", "studenti", "voti.txt");
Path path2 = Paths.get("/home/user/documenti/studenti/voti.txt");
Path path3 = Path.of("./dati/../documenti/voti.txt");
// Informazioni sul path
System.out.println("Nome file: " + path1.getFileName());
System.out.println("Directory: " + path1.getParent());
System.out.println("Root: " + path2.getRoot());
System.out.println("Assoluto: " + path1.toAbsolutePath());
// Normalizzazione (rimuove . e ..)
Path normalizzato = path3.normalize();
System.out.println("Normalizzato: " + normalizzato);
// Risoluzione (combinare path)
Path base = Path.of("/scuola");
Path relativo = Path.of("studenti/mario/voti.txt");
Path completo = base.resolve(relativo);
System.out.println("Path completo: " + completo);
// Relativizzare
Path dir1 = Path.of("/scuola/studenti");
Path dir2 = Path.of("/scuola/docenti/storia");
Path relPath = dir1.relativize(dir2);
System.out.println("Relativo: " + relPath); // ../docenti/storia
// Iterazione componenti
for (Path component : path1) {
System.out.println("Componente: " + component);
}
}
}
Pliki: Operacje na plikach
import java.nio.file.*;
import java.nio.charset.StandardCharsets;
import java.util.*;
public class OperazioniFile {
public static void main(String[] args) throws Exception {
Path file = Path.of("studenti.txt");
// === SCRITTURA ===
// Scrivere stringa completa
String contenuto = "Mario Rossi\nLaura Bianchi\nGiuseppe Verdi";
Files.writeString(file, contenuto);
// Scrivere lista di linee
List<String> linee = Arrays.asList(
"Mario Rossi: 28",
"Laura Bianchi: 30",
"Giuseppe Verdi: 25"
);
Files.write(Path.of("voti.txt"), linee);
// Append
Files.writeString(file, "\nNuovo Studente",
StandardOpenOption.APPEND);
// === LETTURA ===
// Leggere stringa completa
String tuttoContenuto = Files.readString(file);
System.out.println(tuttoContenuto);
// Leggere tutte le linee
List<String> tutteLinee = Files.readAllLines(file);
for (String linea : tutteLinee) {
System.out.println(linea);
}
// Stream di linee (lazy, per file grandi)
Files.lines(file)
.filter(l -> l.contains("Rossi"))
.forEach(System.out::println);
// Leggere bytes
byte[] bytes = Files.readAllBytes(Path.of("documento.pdf"));
}
}
import java.nio.file.*;
import java.nio.file.attribute.*;
public class GestioneFileDirectory {
public static void main(String[] args) throws Exception {
Path dir = Path.of("scuola/studenti");
Path file = dir.resolve("elenco.txt");
// === VERIFICA ESISTENZA E TIPO ===
System.out.println("Esiste: " + Files.exists(file));
System.out.println("È file: " + Files.isRegularFile(file));
System.out.println("È directory: " + Files.isDirectory(dir));
System.out.println("Leggibile: " + Files.isReadable(file));
System.out.println("Scrivibile: " + Files.isWritable(file));
// === CREARE DIRECTORY ===
Files.createDirectory(Path.of("nuova_dir")); // Una sola
Files.createDirectories(dir); // Con genitori
// === CREARE FILE ===
if (!Files.exists(file)) {
Files.createFile(file);
}
// === COPIARE ===
Path copia = Path.of("scuola/backup/elenco_backup.txt");
Files.createDirectories(copia.getParent());
Files.copy(file, copia, StandardCopyOption.REPLACE_EXISTING);
// === SPOSTARE/RINOMINARE ===
Path nuovoPosizione = Path.of("archivio/elenco.txt");
Files.createDirectories(nuovoPosizione.getParent());
Files.move(copia, nuovoPosizione,
StandardCopyOption.REPLACE_EXISTING);
// === ELIMINARE ===
Files.delete(nuovoPosizione); // Eccezione se non esiste
Files.deleteIfExists(nuovoPosizione); // Silenzioso
// === ATTRIBUTI ===
BasicFileAttributes attrs = Files.readAttributes(
file, BasicFileAttributes.class
);
System.out.println("Dimensione: " + attrs.size());
System.out.println("Creato: " + attrs.creationTime());
System.out.println("Modificato: " + attrs.lastModifiedTime());
}
}
Przeglądaj katalogi
import java.nio.file.*;
import java.io.IOException;
public class EsploraDirectory {
public static void main(String[] args) throws IOException {
Path dir = Path.of("scuola");
// === LISTARE CONTENUTI ===
// Lista diretta (non ricorsiva)
System.out.println("=== Contenuto directory ===");
try (var stream = Files.list(dir)) {
stream.forEach(System.out::println);
}
// Lista ricorsiva (walk)
System.out.println("\n=== Walk ricorsivo ===");
try (var stream = Files.walk(dir)) {
stream.forEach(System.out::println);
}
// Walk con profondità massima
try (var stream = Files.walk(dir, 2)) {
stream.filter(Files::isRegularFile)
.forEach(System.out::println);
}
// === CERCARE FILE ===
// Con glob pattern
System.out.println("\n=== File .txt ===");
try (var stream = Files.newDirectoryStream(dir, "*.txt")) {
for (Path file : stream) {
System.out.println(file);
}
}
// Find con predicato
System.out.println("\n=== File > 1KB ===");
try (var stream = Files.find(dir, 10,
(path, attrs) -> attrs.isRegularFile() &&
attrs.size() > 1024)) {
stream.forEach(System.out::println);
}
}
}
Serializacja
Serializacja umożliwia zapisywanie obiektów Java w plikach i przeczytaj je ponownie później.
import java.io.*;
public class Studente implements Serializable {
// UID per versioning
private static final long serialVersionUID = 1L;
private String nome;
private String matricola;
private int annoIscrizione;
// transient = non serializzato
private transient String passwordTemporanea;
public Studente(String nome, String matricola, int annoIscrizione) {
this.nome = nome;
this.matricola = matricola;
this.annoIscrizione = annoIscrizione;
}
// Getters
public String getNome() { return nome; }
public String getMatricola() { return matricola; }
public int getAnnoIscrizione() { return annoIscrizione; }
@Override
public String toString() {
return String.format("Studente[%s, %s, %d]",
nome, matricola, annoIscrizione);
}
}
import java.io.*;
import java.util.*;
public class GestoreSerializzazione {
// Salvare un oggetto
public static void salvaStudente(Studente studente, String file) {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(file))) {
oos.writeObject(studente);
System.out.println("Studente salvato: " + file);
} catch (IOException e) {
System.err.println("Errore salvataggio: " + e.getMessage());
}
}
// Caricare un oggetto
public static Studente caricaStudente(String file) {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(file))) {
return (Studente) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
System.err.println("Errore caricamento: " + e.getMessage());
return null;
}
}
// Salvare lista di oggetti
public static void salvaElenco(List<Studente> studenti, String file) {
try (ObjectOutputStream oos = new ObjectOutputStream(
new FileOutputStream(file))) {
oos.writeObject(studenti);
System.out.println("Elenco salvato: " + studenti.size() + " studenti");
} catch (IOException e) {
System.err.println("Errore: " + e.getMessage());
}
}
@SuppressWarnings("unchecked")
public static List<Studente> caricaElenco(String file) {
try (ObjectInputStream ois = new ObjectInputStream(
new FileInputStream(file))) {
return (List<Studente>) ois.readObject();
} catch (IOException | ClassNotFoundException e) {
System.err.println("Errore: " + e.getMessage());
return Collections.emptyList();
}
}
public static void main(String[] args) {
// Salvare singolo studente
Studente mario = new Studente("Mario Rossi", "MAT001", 2024);
salvaStudente(mario, "mario.dat");
// Caricare
Studente caricato = caricaStudente("mario.dat");
System.out.println("Caricato: " + caricato);
// Salvare lista
List<Studente> studenti = Arrays.asList(
new Studente("Mario Rossi", "MAT001", 2024),
new Studente("Laura Bianchi", "MAT002", 2024),
new Studente("Giuseppe Verdi", "MAT003", 2023)
);
salvaElenco(studenti, "studenti.dat");
// Caricare lista
List<Studente> caricati = caricaElenco("studenti.dat");
caricati.forEach(System.out::println);
}
}
Kompletny przykład: system rejestrów szkolnych
import java.io.*;
import java.nio.file.*;
import java.util.*;
public class RegistroScolastico {
private static final Path DATA_DIR = Path.of("dati_scuola");
private static final Path STUDENTI_FILE = DATA_DIR.resolve("studenti.csv");
private static final Path VOTI_FILE = DATA_DIR.resolve("voti.csv");
public static void inizializza() throws IOException {
Files.createDirectories(DATA_DIR);
if (!Files.exists(STUDENTI_FILE)) {
Files.writeString(STUDENTI_FILE, "matricola,nome,classe\n");
}
if (!Files.exists(VOTI_FILE)) {
Files.writeString(VOTI_FILE, "matricola,materia,voto,data\n");
}
}
public static void aggiungiStudente(String matricola, String nome, String classe)
throws IOException {
String riga = String.format("%s,%s,%s%n", matricola, nome, classe);
Files.writeString(STUDENTI_FILE, riga, StandardOpenOption.APPEND);
System.out.println("Studente aggiunto: " + nome);
}
public static void registraVoto(String matricola, String materia, int voto)
throws IOException {
String data = java.time.LocalDate.now().toString();
String riga = String.format("%s,%s,%d,%s%n", matricola, materia, voto, data);
Files.writeString(VOTI_FILE, riga, StandardOpenOption.APPEND);
System.out.println("Voto registrato: " + materia + " = " + voto);
}
public static void stampaStudenti() throws IOException {
System.out.println("\n=== ELENCO STUDENTI ===");
Files.lines(STUDENTI_FILE)
.skip(1) // Salta header
.forEach(linea -> {
String[] parti = linea.split(",");
if (parti.length >= 3) {
System.out.printf("%-10s %-20s %s%n",
parti[0], parti[1], parti[2]);
}
});
}
public static void stampaVotiStudente(String matricola) throws IOException {
System.out.println("\n=== VOTI di " + matricola + " ===");
Files.lines(VOTI_FILE)
.skip(1)
.filter(linea -> linea.startsWith(matricola + ","))
.forEach(linea -> {
String[] parti = linea.split(",");
if (parti.length >= 4) {
System.out.printf("%-15s %s (%s)%n",
parti[1], parti[2], parti[3]);
}
});
}
public static double calcolaMedia(String matricola) throws IOException {
return Files.lines(VOTI_FILE)
.skip(1)
.filter(linea -> linea.startsWith(matricola + ","))
.mapToInt(linea -> {
String[] parti = linea.split(",");
return Integer.parseInt(parti[2]);
})
.average()
.orElse(0);
}
public static void backupDati() throws IOException {
Path backupDir = DATA_DIR.resolve("backup_" +
java.time.LocalDate.now().toString());
Files.createDirectories(backupDir);
Files.copy(STUDENTI_FILE,
backupDir.resolve("studenti.csv"),
StandardCopyOption.REPLACE_EXISTING);
Files.copy(VOTI_FILE,
backupDir.resolve("voti.csv"),
StandardCopyOption.REPLACE_EXISTING);
System.out.println("Backup creato: " + backupDir);
}
public static void main(String[] args) {
try {
// Inizializza
inizializza();
// Aggiungi studenti
aggiungiStudente("MAT001", "Mario Rossi", "3A");
aggiungiStudente("MAT002", "Laura Bianchi", "3A");
aggiungiStudente("MAT003", "Giuseppe Verdi", "3B");
// Registra voti
registraVoto("MAT001", "Matematica", 28);
registraVoto("MAT001", "Italiano", 26);
registraVoto("MAT001", "Informatica", 30);
registraVoto("MAT002", "Matematica", 30);
registraVoto("MAT002", "Italiano", 29);
// Stampa report
stampaStudenti();
stampaVotiStudente("MAT001");
// Calcola media
double media = calcolaMedia("MAT001");
System.out.printf("%nMedia MAT001: %.2f%n", media);
// Backup
backupDati();
} catch (IOException e) {
System.err.println("Errore I/O: " + e.getMessage());
}
}
}
Najlepsze praktyki
Zasady wydajnego wejścia/wyjścia
- Użyj opcji try-with-resources: Gwarantowane automatyczne zamykanie
- Użyj BufferedReader/Writer: Lepsze występy
- Preferuj NIO.2: Bardziej nowoczesne i przejrzyste API
- Obsługa wyjątków: Wyjątek IOException jest bardzo powszechny
- Określ kodowanie: Użyj StandardCharsets.UTF_8
- Użyj Files.lines() w przypadku dużych plików: Leniwy strumień
- Zawsze zamykaj strumienie: Unikaj wycieków pamięci
- Sprawdź istnienie przed uruchomieniem: Pliki.istnieje()
Wniosek
We/Wy w Javie oferuje potężne narzędzia do pracy z plikami i strumieniami. W przypadku nowoczesnego kodu zalecanym podejściem jest interfejs API NIO.2.
Kluczowe punkty do zapamiętania
- Java.io: Klasyczne strumienie, bajty i znaki
- Buforowany czytnik/zapis: Lepsza wydajność tekstu
- Ścieżka NIO.2: Nowoczesne przedstawienie szlaków
- Pliki NIO.2: Statyczne narzędzia do typowych operacji
- Możliwość serializacji: Aby zapisać obiekty Java
- StandardOpcja otwarta: Precyzyjna kontrola nad otwieraniem plików
Nel następny artykuł będziemy eksplorować programowanie funkcjonalny w Javie: lambda, Stream API i opcjonalne.







