E/S en Java

Table des matières

Introduction à l'IO

Exemple d'E/S

Comment utiliser les flux mis en mémoire tampon pour améliorer les performances

        Exemple de copie de fichier :

        Exemple d'utilisation de la mise en mémoire tampon pour écrire dans un fichier :

Introduction à NIO

introduire

Processus en cours d'exécution Java NIO

Exemples d'applications NIO

Exemple 1 : recevoir la connexion client et envoyer un message au client

Exemple 2 : écoutez les connexions client et faites écho aux messages envoyés par le client au client

Exemple 3 : Accepter plusieurs connexions client et faire écho aux envois du client

Domaines d'application de NIO


Introduction à l'IO

        L'IO (Input/Output, input/output) de Java est un mécanisme de traitement de données qui vous permet de lire des données à partir d'une source externe (entrée) ou d'écrire des données sur une cible externe (sortie). La bibliothèque IO de Java fournit une variété de classes et de méthodes pour effectuer diverses opérations IO dans les programmes Java. Voici quelques bases de Java IO :

  1. Ruisseaux:

    • Stream est l'un des concepts fondamentaux de Java IO, qui représente le canal de transmission de données entre le programme et les périphériques externes.
    • Les flux Java sont divisés en flux d'entrée (utilisés pour lire les données) et flux de sortie (utilisés pour écrire des données).
    • Les classes de flux courantes incluent InputStream, et .OutputStreamReaderWriter
  2. Flux d'octets et flux de caractères :

    • Les flux d'octets sont utilisés pour traiter des données binaires, adaptées aux images, audio, vidéo et autres fichiers.
    • Les flux de caractères (Character Streams) sont utilisés pour traiter des données texte, adaptées aux fichiers texte, etc.
    • Les flux d'octets sont généralement basés sur InputStreamla somme OutputStreamet les flux de caractères sont Readerbasés Writersur la somme.
  3. FichierIO :

    • Les classes Java Filesont utilisées pour manipuler des fichiers et des répertoires. Vous pouvez créer, supprimer, renommer, vérifier si des fichiers existent, etc.
    • Les classes courantes utilisées pour les E/S de fichiers incluent FileInputStream, et .FileOutputStreamFileReaderFileWriter
  4. Flux tamponnés :

    • Les flux tamponnés sont des wrappers autour de flux élémentaires qui améliorent les performances des opérations d'E/S via des tampons internes.
    • Les flux mis en mémoire tampon courants incluent BufferedInputStream, et .BufferedOutputStreamBufferedReaderBufferedWriter
  5. Flux de données :

    • Les flux de données sont utilisés pour lire et écrire des données dans des types de données primitifs plutôt que dans des octets ou des caractères.
    • DataInputStreamet DataOutputStreamsont des classes pour le flux de données.
  6. Sérialisation d'objet :

    • Java fournit un mécanisme de sérialisation d'objets qui permet de conserver les objets sous forme de flux d'octets pour la transmission ou le stockage entre différentes machines virtuelles Java.
    • Les classes utilisées pour la sérialisation d'objets incluent ObjectInputStreamet ObjectOutputStream.
  7. Flux canalisés :

    • Les flux de tuyaux sont utilisés pour la communication entre les threads. Un thread peut écrire des données dans le canal et un autre thread peut lire les données du canal.
    • PipedInputStreamet PipedOutputStreampour les flux d'octets PipedReaderet PipedWriterpour les flux de caractères.
  8. Gestion des exceptions :

    • Lors du traitement des opérations d'E/S, vous devez gérer les exceptions possibles, telles que IOException.
    • Les blocs sont souvent utilisés try-catchpour intercepter et gérer ces exceptions.
  9. Fermez le flux :

    • Une fois les flux utilisés, ils doivent être fermés pour libérer les ressources. Habituellement, close()des méthodes sont utilisées pour fermer le flux.
  10. NIO (Nouvelle IO) :

    • Java fournit le package NIO, qui introduit une méthode de traitement des E/S plus flexible et plus efficace, comprenant des canaux (Channels) et des tampons (Buffers).
    • NIO peut gérer les E/S non bloquantes et convient à la création d'applications réseau hautes performances.

IO est principalement divisé en BIO et NIO

BIO

  • Dans le modèle BIO, les opérations IO sont bloquantes, ce qui signifie que lorsqu'un thread effectue une opération IO, il sera bloqué jusqu'à ce que l'opération soit terminée.
  • Java IO traditionnel (package java.io) est basé sur le modèle BIO, tel que InputStreamet OutputStreametc.
  • BIO convient aux situations dans lesquelles le nombre de connexions est faible et le temps de connexion court, mais il peut entraîner des problèmes de performances dans les environnements à forte concurrence, car chaque connexion nécessite la gestion d'un thread indépendant.

Exemple BIO

        Exemple de lecture de fichier :

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;

public class FileReadExample {
    public static void main(String[] args) {
        String fileName = "example.txt"; // 文件名

        try (BufferedReader reader = new BufferedReader(new FileReader(fileName))) {
            String line;
            while ((line = reader.readLine()) != null) {
                System.out.println(line);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Exemple d'écriture de fichier :

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class FileWriteExample {
    public static void main(String[] args) {
        String fileName = "output.txt"; // 输出文件名
        String content = "Hello, Java IO!";

        try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
            writer.write(content);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Exemple de lecture d'un fichier à l'aide d'un flux tamponné :

import java.io.BufferedInputStream;
import java.io.FileInputStream;
import java.io.IOException;

public class BufferedInputStreamExample {
    public static void main(String[] args) {
        String fileName = "example.bin"; // 二进制文件名

        try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(fileName))) {
            int data;
            while ((data = inputStream.read()) != -1) {
                System.out.print((char) data); // 将读取的字节数据转换为字符输出
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

Introduction à NIO

introduire

        NIO (New I/O, également connu sous le nom d'IO non bloquantes) est un mécanisme Java plus efficace et plus flexible pour gérer les opérations d'E/S. Introduit dans Java 1.4, il fournit un modèle d'E/S non bloquant et piloté par les événements, adapté à la création d'applications d'E/S et de réseau hautes performances et à haute concurrence.

        NIO présente les principales fonctionnalités suivantes par rapport au modèle IO traditionnel (également appelé IO ou blocking IO) :

  1. Opérations non bloquantes : NIO permet aux programmes d'effectuer des opérations d'E/S sans attendre que les données soient prêtes. Le programme peut continuer à effectuer d'autres tâches au lieu d'attendre des données. Cela rend NIO très adapté aux scénarios à haute concurrence qui gèrent plusieurs connexions.

  2. Canal et tampon : NIO introduit les concepts de canal et de tampon. Le canal est responsable de la lecture et de l'écriture des données, et le tampon est responsable du stockage des données. Cette méthode peut gérer les données plus efficacement et réduire les opérations de copie de données.

  3. Sélecteur : Le sélecteur est l'un des composants principaux de NIO. Il permet au programme de surveiller l'état des événements de plusieurs canaux et d'avertir le programme lorsque le canal est prêt. Cette approche basée sur les événements réduit le nombre de threads et améliore les performances du système et l'utilisation des ressources.

  4. Multiplexage : NIO permet à un seul thread de gérer plusieurs canaux, réduisant ainsi la surcharge des threads. Ceci est réalisé grâce à des sélecteurs et des opérations d’E/S non bloquantes.

  5. Fichier IO : NIO convient non seulement à la programmation réseau, mais peut également être utilisé pour les fichiers IO. Il fournit FileChanneldes opérations sur les fichiers pour rendre la lecture et l'écriture de fichiers plus efficaces.

        NIO est largement utilisé dans la création de serveurs réseau hautes performances, de serveurs proxy, d'applications de chat et dans d'autres domaines. Cependant, NIO est plus complexe que le modèle IO traditionnel et nécessite plus de travail de programmation. Il doit donc être manipulé avec prudence pendant le processus de développement.

        NIO de Java comprend des packages tels que java.nioet pour la mise en œuvre d'opérations d'E/S non bloquantes. java.nio.channelsSi vous avez besoin de créer des applications d'E/S hautes performances et à haute concurrence, vous pouvez envisager d'utiliser NIO de Java. De plus, NIO.2 (également connu sous le nom de NIO2 ou Java 7 NIO.2) a été introduit dans les versions ultérieures de Java, améliorant encore les fonctionnalités de NIO.

Processus en cours d'exécution Java NIO

Le processus en cours d'exécution de Java NIO peut être brièvement décrit comme les étapes suivantes :

  1. Créer un sélecteur : commencez par créer un objet Sélecteur. Le sélecteur est au cœur de NIO et est utilisé pour gérer plusieurs événements de canal, tels que l'acceptation de connexions, la lecture de données, etc.

  2. Créer ServerSocketChannel : si vous créez un serveur, vous devez créer un ServerSocketChannel et le lier à un port. ServerSocketChannel est utilisé pour écouter les demandes de connexion des clients.

  3. Enregistrez le canal auprès du sélecteur : enregistrez le ServerSocketChannel auprès du sélecteur et spécifiez les événements à écouter, SelectionKey.OP_ACCEPTindiquant généralement l'écoute des événements de connexion.

  4. En attente qu'un événement se produise : l'appel de la select()méthode Selector sera bloqué jusqu'à ce que l'événement spécifié se produise sur au moins un canal enregistré.

  5. Gestion des événements prêts : une fois select()renvoyée, vous pouvez selectedKeys()obtenir la collection d'événements prêts (collection SelectionKey) via la méthode, puis parcourir ces événements.

  6. Accepter les connexions ou lire les données : s'il y a un événement de connexion, vous pouvez appeler accept()la méthode pour accepter la connexion client, ou s'il y a un événement de lecture, vous pouvez lire les données dans le canal.

  7. Annuler la SelectionKey après le traitement : Après avoir traité un événement, vous devez généralement annuler la SelectionKey correspondante afin de pouvoir continuer à l'écouter la prochaine fois.

  8. Traiter les événements en boucle : répétez les étapes 4 à 7 pour continuer l'écoute et le traitement des événements.

        Ce processus permet au serveur de gérer plusieurs connexions en même temps sans créer de thread distinct pour chaque connexion, améliorant ainsi les performances du serveur et l'utilisation des ressources.

        Il convient de noter que le modèle de programmation de NIO est relativement complexe et nécessite une gestion minutieuse des événements, de l'enregistrement et de l'annulation des chaînes, etc. Habituellement, les développeurs doivent écrire plus de code pour implémenter une application NIO complète, y compris le traitement des protocoles de communication réseau, la lecture et l'écriture des données, la gestion des exceptions, etc. Mais les étapes ci-dessus fournissent un aperçu de base du processus de fonctionnement de NIO.

Exemples d'applications NIO

Exemple 1 : recevoir la connexion client et envoyer un message au client

        Ce qui suit est un exemple simple d'application Java NIO qui montre comment utiliser NIO pour créer un serveur réseau simple capable d'accepter les connexions client et d'envoyer des messages aux clients.

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.util.Iterator;
import java.util.Set;

public class NIOServer {
    public static void main(String[] args) {
        try {
            // 创建一个ServerSocketChannel并设置为非阻塞模式
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.configureBlocking(false);

            // 绑定服务器端口
            serverSocketChannel.socket().bind(new InetSocketAddress(8080));

            // 创建一个选择器(Selector)用于监听通道事件
            Selector selector = Selector.open();

            // 将ServerSocketChannel注册到选择器上,监听ACCEPT事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            System.out.println("服务器启动,监听端口 8080...");

            // 处理事件循环
            while (true) {
                // 选择器等待事件的到来
                selector.select();

                // 获取所有就绪事件的SelectionKey
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

                while (keyIterator.hasNext()) {
                    SelectionKey key = keyIterator.next();

                    if (key.isAcceptable()) {
                        // 有新连接接入,创建SocketChannel处理
                        SocketChannel socketChannel = serverSocketChannel.accept();
                        socketChannel.configureBlocking(false);
                        socketChannel.register(selector, SelectionKey.OP_READ);
                        System.out.println("客户端连接成功:" + socketChannel.getRemoteAddress());
                    } else if (key.isReadable()) {
                        // 通道可读,读取数据并回应
                        SocketChannel socketChannel = (SocketChannel) key.channel();
                        ByteBuffer buffer = ByteBuffer.allocate(1024);
                        int bytesRead = socketChannel.read(buffer);

                        if (bytesRead > 0) {
                            buffer.flip(); // 切换至读模式
                            byte[] data = new byte[bytesRead];
                            buffer.get(data);
                            String message = new String(data, "UTF-8");
                            System.out.println("接收到客户端消息:" + message);

                            // 回应客户端
                            String response = "Hello, client!";
                            ByteBuffer responseBuffer = ByteBuffer.wrap(response.getBytes());
                            socketChannel.write(responseBuffer);
                        }
                    }

                    // 从已选择的集合中移除当前处理的SelectionKey
                    keyIterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

        Cet exemple crée un simple serveur NIO qui écoute sur le port 8080 et peut accepter les connexions client et traiter les messages envoyés par les clients. Pour exécuter cet exemple, vous pouvez créer une application Java et coller le code ci-dessus dans mainla méthode.

        Il convient de noter qu'il ne s'agit que d'un simple exemple NIO, un véritable serveur de niveau production peut nécessiter une logique et une gestion des erreurs plus complexes. De plus, l'utilisation de NIO nécessite une gestion minutieuse de la gestion des tampons, de la synchronisation multithread et d'autres problèmes. Dans le développement réel, vous pouvez envisager d'utiliser le framework ou la bibliothèque NIO, telle que Netty, pour simplifier la programmation réseau et améliorer les performances.

Exemple 2 : écoutez les connexions client et faites écho aux messages envoyés par le client au client

        Ce qui suit est un exemple simple d'application Java NIO qui montre comment utiliser NIO pour créer un serveur simple qui écoute les connexions client et renvoie les messages envoyés par le client au client.

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.SelectionKey;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.Selector;
import java.util.Iterator;
import java.util.Set;

public class NIOServer {
    public static void main(String[] args) {
        try {
            // 创建一个Selector
            Selector selector = Selector.open();

            // 创建ServerSocketChannel并绑定端口
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress(8080));
            serverSocketChannel.configureBlocking(false);

            // 注册ServerSocketChannel到Selector,并监听ACCEPT事件
            serverSocketChannel.register(selector, SelectionKey.OP_ACCEPT);

            System.out.println("Server started on port 8080");

            while (true) {
                // 阻塞等待就绪的事件
                int readyChannels = selector.select();

                if (readyChannels == 0) {
                    continue;
                }

                // 获取就绪事件的SelectionKey集合
                Set<SelectionKey> selectedKeys = selector.selectedKeys();
                Iterator<SelectionKey> keyIterator = selectedKeys.iterator();

                while (keyIterator.hasNext()) {
                    SelectionKey key = keyIterator.next();

                    if (key.isAcceptable()) {
                        // 客户端连接事件
                        acceptClientConnection(serverSocketChannel, selector);
                    } else if (key.isReadable()) {
                        // 客户端数据可读事件
                        readAndEchoData(key);
                    }

                    // 移除处理过的SelectionKey
                    keyIterator.remove();
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void acceptClientConnection(ServerSocketChannel serverSocketChannel, Selector selector) throws IOException {
        // 接受客户端连接
        SocketChannel clientChannel = serverSocketChannel.accept();
        clientChannel.configureBlocking(false);

        // 注册客户端通道到Selector,并监听读事件
        clientChannel.register(selector, SelectionKey.OP_READ);
        System.out.println("Accepted connection from: " + clientChannel.getRemoteAddress());
    }

    private static void readAndEchoData(SelectionKey key) throws IOException {
        SocketChannel clientChannel = (SocketChannel) key.channel();
        ByteBuffer buffer = ByteBuffer.allocate(1024);

        // 读取客户端发送的数据
        int bytesRead = clientChannel.read(buffer);

        if (bytesRead > 0) {
            buffer.flip();
            byte[] data = new byte[bytesRead];
            buffer.get(data);

            // 打印接收到的数据
            System.out.println("Received: " + new String(data));

            // 回显数据给客户端
            clientChannel.write(ByteBuffer.wrap(data));
        } else {
            // 客户端关闭连接
            clientChannel.close();
            System.out.println("Connection closed by client.");
        }
    }
}

        Cet exemple crée un simple serveur NIO qui écoute sur le port 8080, accepte les connexions client et renvoie les messages envoyés par le client au client. Les concepts clés incluent Selector, ServerSocketChannel, SocketChannel et SelectionKey. En utilisant des sélecteurs, le serveur peut gérer plusieurs connexions simultanément sans avoir besoin de créer un thread pour gérer chaque connexion, améliorant ainsi les performances.

        Veuillez noter qu'il ne s'agit que d'un simple exemple de démonstration, les applications NIO réelles peuvent impliquer une logique plus complexe et du multi-threading. Mais cet exemple fournit un cadre de base pour comprendre comment créer des applications serveur à l'aide de Java NIO.

Exemple 3 : Accepter plusieurs connexions client et faire écho aux envois du client

        Ce qui suit est un exemple simple d'application Java NIO qui montre comment utiliser NIO pour implémenter un serveur réseau simple pouvant accepter plusieurs connexions client et des messages d'écho envoyés par les clients.

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ServerSocketChannel;
import java.nio.channels.SocketChannel;
import java.nio.charset.StandardCharsets;
import java.net.InetSocketAddress;

public class NIOServer {
    public static void main(String[] args) {
        try {
            // 创建服务器Socket通道
            ServerSocketChannel serverSocketChannel = ServerSocketChannel.open();
            serverSocketChannel.socket().bind(new InetSocketAddress(8080));

            while (true) {
                // 等待客户端连接
                SocketChannel clientChannel = serverSocketChannel.accept();

                // 处理客户端连接
                handleClient(clientChannel);
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void handleClient(SocketChannel clientChannel) throws IOException {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        String welcomeMessage = "Welcome to the NIO Server! Type 'exit' to quit.\n";
        buffer.clear();
        buffer.put(welcomeMessage.getBytes(StandardCharsets.UTF_8));
        buffer.flip();
        clientChannel.write(buffer);

        while (true) {
            buffer.clear();
            int bytesRead = clientChannel.read(buffer);

            if (bytesRead == -1) {
                // 客户端关闭连接
                break;
            }

            buffer.flip();
            byte[] data = new byte[buffer.remaining()];
            buffer.get(data);

            String message = new String(data, StandardCharsets.UTF_8).trim();
            System.out.println("Received from client: " + message);

            // 回显消息给客户端
            buffer.clear();
            buffer.put(("Server: " + message + "\n").getBytes(StandardCharsets.UTF_8));
            buffer.flip();
            clientChannel.write(buffer);
        }

        // 关闭客户端连接
        clientChannel.close();
    }
}

        Cet exemple simple de serveur NIO est utilisé pour écouter sur un port spécifié (8080) et est créé pour gérer la connexion ServerSocketChannellorsqu'un client se connecte . SocketChannelLe serveur envoie un message de bienvenue au client et renvoie le message envoyé par le client au client. Le client peut communiquer avec le serveur en tapant un message et en appuyant sur Entrée, et en tapant « exit » peut fermer le client.

        Ceci n'est qu'un exemple simple, les applications NIO réelles peuvent être plus complexes, impliquant du multithreading, une gestion d'événements non bloquante et des protocoles plus complexes. Mais cet exemple démontre les principes de base et l'utilisation de NIO pour vous aider à démarrer avec la programmation NIO.

Domaines d'application de NIO

        Java NIO (New I/O) est largement utilisé dans divers domaines et technologies, en particulier lors de la création de systèmes hautes performances et à haute concurrence. Voici quelques exemples de technologies et de domaines d'application utilisant Java NIO :

  1. Programmation réseau : l'une des utilisations les plus courantes de NIO concerne la programmation réseau, en particulier la création de serveurs et de clients réseau hautes performances. Par exemple, les bibliothèques réseau telles que gRPC, Netty et Apache MINA utilisent largement NIO pour mettre en œuvre une communication réseau asynchrone et non bloquante.

  2. Middleware de messages : de nombreux middlewares de messages, tels qu'Apache Kafka et RabbitMQ, utilisent NIO pour gérer la transmission et la distribution des messages. NIO peut améliorer les performances et le débit du middleware de messages.

  3. Pool de connexions à la base de données : certaines bibliothèques de pools de connexions à la base de données utilisent NIO pour gérer les connexions à la base de données, améliorant ainsi l'efficacité de l'accès à la base de données. Par exemple, HikariCP est un pool de connexions à une base de données hautes performances utilisant NIO.

  4. Traitement de fichiers : NIO convient également au traitement de fichiers, notamment à la lecture et à l'écriture de fichiers volumineux. En l'utilisant FileChannel, des opérations d'E/S efficaces peuvent être réalisées dans le traitement des fichiers.

  5. Systèmes distribués : lors de la création de systèmes distribués, NIO peut être utilisé pour la communication entre les nœuds. Par exemple, Apache ZooKeeper utilise NIO pour gérer la communication du cluster.

  6. Développement de jeux : de nombreux jeux en ligne et serveurs de jeux utilisent NIO pour gérer la communication en temps réel entre les joueurs. Les caractéristiques de faible latence de NIO en font un choix judicieux dans le domaine des jeux.

  7. Framework Web : certains frameworks Web, tels que Spring Webflux, utilisent NIO pour implémenter le traitement asynchrone des requêtes Web afin d'améliorer les performances simultanées des applications Web.

  8. Serveur HTTP : certains serveurs HTTP, tels que Tomcat et Undertow, prennent en charge le mode NIO pour gérer un grand nombre de requêtes HTTP simultanées.

  9. Traitement des données en temps réel : Dans le domaine du traitement des données en temps réel, tel que la transmission et le traitement des données en temps réel dans le domaine financier, NIO est également souvent utilisé.

        Ce ne sont là que quelques exemples, NIO dispose d'une large gamme d'applications et vous pouvez envisager de l'utiliser dans presque toutes les situations où des opérations d'E/S hautes performances et à haute concurrence sont requises. Cependant, il convient de noter que la programmation NIO est relativement complexe et nécessite une gestion minutieuse des événements et des états, elle nécessite donc une certaine expérience et compétences pour l'utiliser correctement.

flux mis en mémoire tampon

        Le flux tamponné est un type spécial de flux d'E/S Java utilisé pour améliorer les performances d'E/S. Il est généralement utilisé pour envelopper d'autres flux d'entrée et flux de sortie sous-jacents afin de réduire le nombre d'opérations d'E/S et ainsi d'améliorer l'efficacité de la lecture et de l'écriture des données. Ils peuvent être utilisés dans les modèles BIO (Blocking IO) et NIO (New IO), non seulement pour accélérer les E/S de fichiers, mais également pour lire et écrire des données dans les communications réseau.

        BufferedInputStream : utilisé pour encapsuler les flux d'entrée (tels que FileInputStream, SocketInputStream, etc.). Il lit les données du flux d'entrée sous-jacent dans un tampon interne, puis lit les données du tampon octet par octet ou sous forme de tableau d'octets d'une certaine longueur.

try (FileInputStream fileInputStream = new FileInputStream("example.txt");
     BufferedInputStream bufferedInputStream = new BufferedInputStream(fileInputStream)) {

    int byteRead;
    while ((byteRead = bufferedInputStream.read()) != -1) {
        // 处理读取的数据
    }
} catch (IOException e) {
    e.printStackTrace();
}

        BufferedOutputStream : utilisé pour encapsuler les flux de sortie (tels que FileOutputStream, SocketOutputStream, etc.). Il écrit les données dans un tampon interne, puis les écrit dans le flux de sortie sous-jacent uniquement lorsque le tampon est plein ou vidé.

try (FileOutputStream fileOutputStream = new FileOutputStream("example.txt");
     BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(fileOutputStream)) {

    byte[] data = "Hello, BufferedOutputStream!".getBytes();
    bufferedOutputStream.write(data);
    bufferedOutputStream.flush(); // 手动刷新缓冲区
} catch (IOException e) {
    e.printStackTrace();
}

        BufferedReader : utilisé pour encapsuler les flux d'entrée de caractères (tels que FileReader, InputStreamReader, etc.). Il fournit des méthodes de lecture de caractères de niveau supérieur, ainsi que la possibilité de lire des données de texte ligne par ligne.

try (FileReader fileReader = new FileReader("example.txt");
     BufferedReader bufferedReader = new BufferedReader(fileReader)) {

    String line;
    while ((line = bufferedReader.readLine()) != null) {
        // 处理读取的行数据
    }
} catch (IOException e) {
    e.printStackTrace();
}

        BufferedWriter : utilisé pour encapsuler les flux de sortie de caractères (tels que FileWriter, OutputStreamWriter, etc.). Il fournit des méthodes d'écriture de caractères de niveau supérieur, ainsi que la possibilité d'écrire des données de texte ligne par ligne.

try (FileWriter fileWriter = new FileWriter("example.txt");
     BufferedWriter bufferedWriter = new BufferedWriter(fileWriter)) {

    String data = "Hello, BufferedWriter!";
    bufferedWriter.write(data);
    bufferedWriter.newLine(); // 换行
} catch (IOException e) {
    e.printStackTrace();
}

        L'avantage des flux mis en mémoire tampon est qu'ils peuvent réduire le nombre d'opérations d'E/S, améliorant ainsi l'efficacité de la lecture et de l'écriture des données. En particulier lors du traitement de grandes quantités de données ou d'opérations d'E/S fréquentes, l'utilisation de flux mis en mémoire tampon peut améliorer considérablement les performances. Par conséquent, il est généralement recommandé d’utiliser des flux mis en mémoire tampon lors des E/S de fichiers ou des communications réseau.

Comment utiliser les flux mis en mémoire tampon pour améliorer les performances

        Exemple de copie de fichier :

import java.io.*;

public class FileCopyWithBufferedStreams {
    public static void main(String[] args) {
        String sourceFile = "source.txt";
        String destinationFile = "destination.txt";

        try (BufferedInputStream inputStream = new BufferedInputStream(new FileInputStream(sourceFile));
             BufferedOutputStream outputStream = new BufferedOutputStream(new FileOutputStream(destinationFile))) {

            byte[] buffer = new byte[4096]; // 创建一个缓冲区
            int bytesRead;

            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead); // 将缓冲区的数据写入目标文件
            }

            System.out.println("文件复制完成!");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

        Dans cet exemple, nous utilisons BufferedInputStreampour lire les données du fichier source et BufferedOutputStreampour écrire les données dans le fichier cible. La taille du tampon peut généralement être ajustée en fonction des besoins réels. Des tampons plus grands améliorent généralement les performances, mais utilisent également plus de mémoire.

        En utilisant des flux mis en mémoire tampon, les données seront mises en mémoire tampon, réduisant ainsi les opérations d'E/S fréquentes et améliorant ainsi l'efficacité des opérations de copie. Ceci est utile pour gérer des fichiers volumineux ou des transferts de données réseau. Il est important de noter qu’après avoir utilisé les flux mis en mémoire tampon, ils doivent être fermés rapidement pour libérer les ressources.

        Exemple d'utilisation de la mise en mémoire tampon pour écrire dans un fichier :

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;

public class BufferedWriteExample {
    public static void main(String[] args) {
        String fileName = "output.txt"; // 输出文件名
        String content = "Hello, Buffered IO!";

        try (BufferedWriter writer = new BufferedWriter(new FileWriter(fileName))) {
            writer.write(content);
            // 注意:虽然我们是一次写入的,但缓冲流内部可能会将数据缓冲一段时间后才写入磁盘,以提高性能。
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

        Dans cet exemple, BufferedWriterun tampon est conservé en interne et plusieurs caractères peuvent être écrits simultanément, améliorant ainsi l'efficacité de l'écriture des fichiers.

Vous devez faire attention aux points suivants lorsque vous utilisez des flux mis en mémoire tampon :

  • L'amélioration des performances des flux mis en mémoire tampon est généralement plus évidente lors d'un grand nombre d'opérations de lecture et d'écriture, et peut ne pas apporter beaucoup d'amélioration des performances aux opérations d'E/S avec de petites quantités de données.
  • Utilisez try-with-resourcesdes instructions (Java 7 et versions ultérieures) pour garantir que les flux mis en mémoire tampon sont automatiquement fermés lorsque vous avez fini de les utiliser.
  • En interne, le flux de tampon actualisera automatiquement (écrira sur le disque) les données dans le tampon selon les besoins, mais vous pouvez également utiliser flush()des méthodes pour actualiser manuellement le tampon.
  • Vous pouvez définir la taille du tampon selon vos besoins. De manière générale, des tampons plus grands améliorent les performances mais occupent plus de mémoire.

        En bref, l'utilisation de flux mis en mémoire tampon peut améliorer efficacement les performances des opérations d'E/S, en particulier lors du traitement de grandes quantités de données. Pendant le développement, choisissez la classe de flux de tampon appropriée (telle que BufferedReaderou BufferedWriter) et la taille de tampon appropriée en fonction des besoins réels.

Guess you like

Origin blog.csdn.net/Deikey/article/details/132667082