IO/NIO Ввод-вывод

 

IO/NIO Ввод-Вывод

Потоковый ввод-вывод (java.io)

  • InputStream - читать байты
  • OutputStream - писать байты
  • Reader - читать символы
  • Writer - писать символы

InputStream

  • int read() -> -1 или 0..255
  • int read(byte[] b) -> кол-во прочитанных байт
  • int read(byte[] b, int offset, int length) -> кол-во прочитанных байт
  • long skip(long n) -> кол-во пропущенных байт
  • int available() -> кол-во байт доступных без блокировки
  • void mark(int readlimit)
  • void reset()
  • boolean markSupported()
  • Java 9 и выше
  • byte[] readAllBytes() -> все содержимое. Java 9+
  • byte[] readNBytes(int len) -> не больше len байт. Java 11+
  • byte[] readNBytes(byte[] b, int offset, int length) -> кол-во прочитанных байт до ошибки или конца потока. Java 11+
  • void skipNBytes(long n) -> пропускает n байт
  • long transferTo(OutputStream out) -> перекачивает в выходной поток. Java 9+
  • static InpytStream nullInputStream() -> пустой поток. Java 11+

Реализации InputStream

  • FileInputStream - файл
  • ByteArrayInputStream - массив байт
  • BufferedInputStream - буферизированная обертка
  • DataInputStream - структурированные двоичные данные
  • PipedInputStream - плохой, негодный
  • ZipFile.getInputStream(ZipEntry) - файл из зипа (без распаковки!)
  • Process.getInputStream() - стандартный вывод процесса
  • URL.openStream() - содержимое по URL(http/https/ftp/file/etc)
  • ….

Пример InputStream
Вычитать все в массив заданного размера

class Main {
    static void readFully(InputStream is, byte[] b) throws IOException {
        int offset = 0;
        while (offset < b.length) {
            int count = is.read(b, offset, b.length-offset);
            if (count == -1) {
                throw new IOException("Stream has less than " + b.length + " bytes");
            }
            offset += count;
        }
    }

    public static void main(String[] args) throws IOException {
      byte[] b = new byte[100];
      readFully(System.in, b);
      System.out.println(Arrays.toString(b));
    }
}

URL (HTTP-клиент для бедных)

class Main {
  public static void main(String[] args) {
    InputStream is = new URL("http://www.google.com").openStream();
    System.out.println(new String(is.readAllBytes(), StandardCharsets.UTF_8));
  }
}

HttpClient (Java 11) - гибко

class Main {
  public static void main(String[] args) {
    HttpClient client = HttpClient.newHttpClient();
    HttpRequest request = HttpRequest.newBuilder()
            .uri(URI.create("http://google.com/"))
            .build();
    client.sendAsync(request, HttpResponse.BodyHandlers.ofString())
            .thenApply(HttpResponse::body)
            .thenAccept(System.out::println)
            .join();
  }
}

Закрываем явно

Пример:

/* НЕ правильный подход*/
class Main {
  public static void main(String[] args) {
    InputStream f = new FileInputStream("/etc/passwd");
    int b = f.read();
    f.close();
  }
}
/* Правильный подход */
class Main {
  public static void main(String[] args) {
    InputStream f = new FileInputStream("/etc/passwd");
    try {
      int b = f.read();
    } finally {
        f.close();
    }
  }
}
/* Лучший подход */
class Main {
  public static void main(String[] args) {
    try(InputStream in = new FileInputStream("/etc/passwd");
        OutputStream out = new FileOutputStream("/etc/passwd.bak")) {
        in.transferTo(out);
    }
  }
}

OutputStream

  • void write(int b) -> пишет 8 младших бит
  • void write(byte[] b) -> пишет весь массив (если не упал, то все записал)
  • void write(byte[]b, int offset, int length) -> пишет часть массива
  • void flush()
  • void close()
  • static OutputStream nullOutputStream() -> пишет сколько влезет (Java 11)

Реализации OutputStream

  • FileOutputStream - файл
  • ByteArrayOutputStream - массив байт
  • BufferedOutputStream - буферизированная обертка
  • PrintStream - для удобства печати (System.out)
  • DataOutputStream - структурированные двоичные данные
  • PipedOutputStream - плохой (не использовать)
  • Process.getOutputStream() - стандартный ввод процесса
  • URL.openConnection().getOutputStream() - писать в URL (HTTP POST)
  • ….

Пример OutputStream

class Main {
  public static void main(String[] args) {
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    baos.write(0x01);
    baos.write(0x02);
    baos.write(0x03);
    byte[] result = baos.toByteArray();
    System.out.println(Arrays.toString(result));    // [1, 2, 3]
  }
}

Reader

  • int read() -> -1 или 0..0xFFFF
  • int read(char[] cb) -> кол-во прочитанных символов
  • int read(char[] cb, int offset, int length) -> кол-во прочитанных символов
  • long skip(long n) -> кол-во пропущенных символов
  • boolean ready() -> можно ли прочитать хоть что-то без блокировки
  • void mark(int readlimit)
  • void reset()
  • boolean markSupported()
  • long transferTo(Write out) - Java 10
  • static Reader nullReader() - Java 11

Реализации Reader

  • InputStreamReader - InputStream + charset
  • FileReader - осторожно
    • Java 11: FileReader(file, charset)
    • Java 18: UTF-8 by default
  • StringReader
  • BufferedReader extends Reader
    • String readLine()
    • Stream< String >lines()
  • ….

Writer

  • void write(int b) -> пишет 8 младших бит
  • void write(char[] cb) -> пишет весь массив (если не упал, то все записал)
  • void write(char[]cb, int offset, int length) -> пишет часть массива
  • void write(String str) - пишет всю строку, если не упал
  • void write(String str, int offset, int length) - пишет часть строки
  • void flush()
  • void close()
  • static Write nullWriter() -> пишет, сколько влезет (Java 11)

Реализации Writer

  • OutputStreamReader - OutputStream + charset
  • FileWriter - осторожно
    • Java 11: FileWriter(file, charset)
    • Java 18: UTF-8 by default
  • StringWriter
  • BufferedWriter

java.io.File

  • new File(parentDirectory + “/” + fileName);~~
  • new File(parentDirectory + File.separator + fileName);
  • new File(parentDirectory, fileName);
  • Mетоды
  • getName()/getPath()/getParent()/getParentFile()
  • getAbsoluteFile()/getAbsolutePath()
  • getCanonicalFile()/getCanonocalPath()
  • exists()/canRead()/canWrite()/canExecute()/length()
  • isDirectory()/isFile()/isHidden()
  • createNewFile()/mkdir()/mkdirs()
  • renameTo()/delete()
  • list([filter])/listFiles([filter])

java.nio.file.Path/Paths/Files

  • Path p = Paths.get("/etc/passwd");
  • Path p = Paths.get(parentDirectory, fileName);
  • Path p = Paths.get("foo", "bar", "baz");
  • Java 11+
  • Path p = Path.of("/etc/passwd");
  • Path p = Path.of(parentDirectory, fileName);
  • Path p = Path.of("foo", "bar", "baz");
  • Path (Comparable)
    • getFileName()
    • getParent()
    • getRoot()
    • resolve(Path/String), resolveSibling(Path/String)
    • isAbsolute(), toAbsolutePath()
    • startsWith(Path/String), endsWith(Path/String)
    • getName(int), getNameCount(), iterator()
    • toString(), toFile(), toUri()
    • register(WatchService, WatchEvent.Kind...)
  • Files
    • copy()
    • move()
    • delete(), deleteIfExist()
    • createFile(), createDirectory(), createDirectories()
    • createLink(), createSimbolicLink()
    • createTempFile(), createTempDirectory()
    • readAllBytes(), readAllLines()
    • write(byte[], iterable<String>)
    • lines, list, walk -> Stream
    • walkFileTree()
    • exist, size, getAttribute, isDirectory, isRegularFile, isSimbolicLink...
    • newBufferedReader, newInputStream, newOutputStream, newByteChannel

Примеры:

import java.io.File;

/* Пример плохого кода */
public class Listing {
  public static void main(String[] args) {
    var etcDirectory = new File("/etc");
    for (String fileName : etcDirectory.list()) {
      System.out.println("File: " + fileName);
    }
  }
} 
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;

/* Пример допустимого кода */
public class Listing {
  public static void main(String[] args) throws IOException {
    var etcDirectory = Paths.get("/etc");
    try (var stream = Files.list(etcDirectory)) {
      stream.forEach(fileName -> System.out.println("File: " + fileName));
    }
  }
}