Prefácio
No artigo anterior, introduzimos o tipo de arquivo de arquivos de disco abstrato. Ele é usado apenas para descrever abstrivelmente um arquivo ou diretório de disco, mas não possui a capacidade de acessar e modificar o conteúdo de um arquivo.
Java IO Stream é um design usado para ler e gravar conteúdo de arquivo. Ele pode preencher a transferência de dados do conteúdo do arquivo de disco de saída nos dados de memória ou saída de memória para arquivos de disco.
O design dos riachos Java IO não é perfeito. Ele projetou um grande número de classes, que aumentou nossa compreensão dos fluxos de IO, mas existem apenas duas categorias principais: um é os fluxos de bytes para arquivos binários e o outro são fluxos de caracteres para arquivos de texto. Neste artigo, primeiro aprenderemos os princípios e cenários de uso de tipos relacionados de fluxos de bytes. Os tipos de fluxo específicos envolvidos principalmente são os seguintes:
Classe Base Byte Stream Input/OutputStream
InputStream e OutputStream são as classes base para fluxos de bytes de leitura e fluxos de bytes de escrita, respectivamente. Todos os córregos relacionados a bytes devem herdar de qualquer um deles. Como uma classe abstrata, eles também definem as operações mais básicas de leitura e gravação. Vamos dar uma olhada:
Tome o InputStream como exemplo:
Public Abstract Int Read () lança IoException;
Este é um método abstrato e não fornece uma implementação padrão, exigindo que as subclasses sejam implementadas. O objetivo deste método é retornar o próximo byte do arquivo atual para você.
Obviamente, você também descobrirá que o valor de retorno desse método é recebido usando o tipo inteiro "int", então por que não usar "byte"?
Primeiro de tudo, o valor retornado pelo método de leitura deve ser um binário de oito bits, e o intervalo de valor que pode ser levado por um binário de oito bits é: "0000 0000, 1111 1111", ou seja, o intervalo [-128, 127].
O método de leitura também especifica que, quando o arquivo é lido até o final, ou seja, o arquivo não tem o próximo byte para leitura, o valor -1 será retornado. Portanto, se o byte for usado como o tipo de valor de retorno, quando o método retornar A -1, devemos determinar se esse é o conteúdo de dados no arquivo ou no final do fluxo?
O tipo int ocupa quatro bytes, e os três bytes no alto são todos 0. Usamos apenas seu bit mais baixo. Ao encontrar o final do sinalizador de fluxo, ele retorna -1 (32 1s) representado por quatro bytes, que é naturalmente diferente do valor -1 (24 0 + 8 1s) representando os dados.
Em seguida, também é um método de leitura, mas o InputStream fornece uma implementação padrão:
public int read (byte b []) lança ioexception {return read (b, 0, b.length);} public int read (byte b [], int off, int len) lança ioexception {// Para não fazer o comprimento por muito tempo, você pode ver o código -fonte JDK por si mesmo}Esses dois métodos são essencialmente os mesmos. O primeiro método é um formulário especial do segundo método, que permite que uma matriz de bytes seja transmitida e exige que o programa preencha os bytes lidos no arquivo que inicia a posição do índice da matriz 0 para preencher o número de bytes no comprimento da matriz.
O segundo método é um pouco mais amplo, o que permite especificar a posição inicial e o número total de bytes.
Existem vários outros métodos no InputStream, que basicamente não são implementados em detalhes. Vamos dar uma olhada brevemente.
O método Mark marcará um sinalizador na posição atual de leitura do fluxo e o método de redefinição redefinirá o ponteiro de leitura para o sinalizador.
De fato, é impossível redefinir a leitura de volta para leitura de arquivos, mas geralmente todos os bytes entre a posição do sinalizador e o ponto de redefinição são salvos temporariamente. Quando o método de redefinição é chamado, ele é na verdade uma leitura repetida do conjunto de bytes temporários salvos; portanto, o Readlimit é usado para limitar a capacidade máxima de cache.
O método MarkSupported é usado para determinar se o fluxo atual suporta esta operação de leitura "Fallback".
O outputstream e o InputStream são semelhantes, exceto que um está escrito e o outro é lido. Não vamos repeti -lo aqui.
Arquivo byte stream fileInput/outputStream
Ainda estamos focados no FileInputStream, e o FileOutputStream é semelhante.
Primeiro, o FileInputStream possui os seguintes construtores para instanciar um objeto:
public fileInputStream (nome da string) lança fileNotfoundException {this (name! = null? Novo File (nome): null);} public fileInputStream (arquivo de arquivo) lança fileNotfoundException {string name = (file! = null? file.getPath (): null); SecurityManager Security = System.getSecurityManager (); if (segurança! = null) {Security.CheckRead (nome); } if (name == null) {lança new nullPointerException (); } if (file.isInValid ()) {lança novo fileNotfoundException ("caminho do arquivo inválido"); } fd = new FileDescriptor (); fd.attach (isto); caminho = nome; aberto (nome);}Esses dois construtores são essencialmente os mesmos, o primeiro é a forma especial deste último. De fato, não olhe para o último método, a maioria dos quais está apenas fazendo verificação de segurança. O núcleo é um método aberto, usado para abrir um arquivo.
Principalmente esses dois construtores, se o arquivo não existir ou o caminho e o nome do arquivo forem ilegais, uma FileNotFoundException será lançada.
Lembre -se de que dissemos que existe um método abstrato lido na classe Base InputStream que exige que todas as subclasses sejam implementadas, e o FileInputStream é implementado usando um método local:
public int read () lança ioexception {return read0 ();} private nativo int read0 () lança ioexception;Não temos como explorar a implementação específica do Read0 por enquanto, mas você deve estar claro que a função desse método de leitura é usada para retornar o próximo byte no fluxo e retornar -1. Isso significa que é lido no final do arquivo e não há bytes para ler.
Além disso, existem outros métodos relacionados à leitura no FileInputStream, mas a maioria deles é implementada usando métodos locais. Vamos dar uma olhada aqui:
Os métodos internos do FileInputStream são basicamente assim, e existem alguns avançados e complexos que não podemos usar por enquanto. Vamos aprender mais tarde. Vamos dar uma breve olhada em um exemplo de leitura de arquivos:
public static void main (string [] args) lança IoException {FileInputStream input = new FileInputStream ("c: //users//yanga//desktop//test.txt"); byte [] buffer = novo byte [1024]; int len = input.read (buffer); String str = new string (buffer); System.out.println (str); System.out.println (len); input.close ();}O resultado da saída é muito simples. Ele imprimirá o conteúdo em nosso arquivo de teste e o número real de bytes lidos, mas os alunos cuidadosos descobrirão, como você pode garantir que o conteúdo no arquivo de teste não exceda 1024 bytes?
Para ler completamente o conteúdo do arquivo, uma solução é definir o buffer grande o suficiente para esperar armazenar todo o conteúdo do arquivo o máximo possível.
Esse método é obviamente indesejável, porque é impossível percebermos o tamanho real do arquivo a ser lido. É uma solução muito ruim para simplesmente criar uma matriz de bytes de grandes dimensões.
A segunda maneira é usar nosso fluxo dinâmico de matriz de bytes, que pode ajustar dinamicamente o tamanho da matriz de bytes interna para garantir a capacidade apropriada, que apresentaremos em detalhes posteriormente.
Em relação ao FileOutputStream, mais uma coisa a enfatizar é o seu construtor, que possui os dois construtores a seguir:
public fileOutputStream (nome da string, apêndice booleano) public FileOutputStream (arquivo de arquivo, apêndice booleano)
O apendamento do parâmetro indica se a operação de gravação desse fluxo é substituída ou anexada, true significa anexado, false significa substituição.
ByteArrayInput/outputStream
O chamado "Stream de matriz de bytes" é um fluxo que opera em torno de uma matriz de bytes. Ele não lê e grava fluxos em arquivos como outros fluxos.
Embora o fluxo de matriz de bytes não seja um fluxo baseado em arquivo, ele ainda é um fluxo muito importante, porque a matriz de bytes encapsulada interna não é fixa, mas dinamicamente extensível e geralmente é baseada em determinados cenários, o que é muito adequado.
ByteArrayInputStream é um fluxo de matrizes de leitura de bytes que podem ser instanciadas pelo seguinte construtor:
byte protegido buf []; protegido int pos; protegido int contagem; public bytearrayInputStream (byte buf []) {this.buf = buf; this.pos = 0; this.count = buf.length;} public bytearrayInputStream (byte buf [], Int Offset, Int Length)BUF é uma matriz de bytes encapsulada dentro do bytearrayInputStream. Todas as operações de leitura do ByteArrayInputStream giram em torno dele.
Portanto, ao instanciar um objeto bytearrayInputStream, pelo menos uma matriz de bytes de destino é transmitida.
O atributo PDV é usado para gravar a posição da leitura atual do fluxo e a contagem registra a última posição do último índice de bytes válido da matriz de bytes de destino.
Depois de entender isso, não é difícil ler várias maneiras de lê -lo:
// leia o próximo byte público sincronizado int read () {return (pos <count)? (BUF [POS ++] & 0XFF): -1;} // Leia Len Bytes e coloque -os na matriz B public sincronizada Int Read (byte B [], int Off, int len) {// O mesmo, o corpo do método é mais longo, todo mundo verifica seu próprio JDK}Além disso, o ByteArrayInputStream também implementa a operação de "repetição de leitura" de maneira muito simples.
public void Mark (int readeHeadlimit) {mark = pos;} public sincronizado void reset () {pos = mark;}Como o ByteArrayInputStream é baseado em matrizes de bytes, todas as operações de leitura repetidas são mais fáceis de implementar e é suficiente para implementar com base nos índices.
BytearrayOutputStream é um fluxo de matriz de bytes escrito. Muitas implementações ainda têm suas próprias características. Vamos dar uma olhada juntos.
Primeiro, essas duas propriedades são necessárias:
byte protegido buf []; // a contagem aqui representa o número de bytes válidos na contagem int protegida por BUF;
Construtor:
public byteArrayOutputStream () {this (32);} public byteArrayOutputStream (int size) {if (size <0) {lança novo ilegalArgumentException ("tamanho inicial negativo:"+ tamanho); } buf = novo byte [size];}A tarefa principal do construtor é inicializar o BUF de matriz de bytes interno, permitindo que você passe em tamanho para limitar explicitamente o tamanho da matriz de bytes inicializado, caso contrário, o comprimento padrão será 32.
Escreva conteúdo para bytearrayoutputStream de fora:
public sincronizado void write (int b) {surCapacity (contagem + 1); BUF [count] = (byte) b; contagem + = 1;} public sincronizado void write (byte b [], int off, int len) {if ((off <0) || (off> b.Length) || (len <0) || ((off + len) - b.length> 0)) {tiro o novo indexOutfBoundSexception (); } garantir (contagem + len); System.arraycopy (B, Off, BUF, Conde, Len); contagem += len;}Vendo que, a primeira etapa de todas as operações de gravação é chamar o método de segurança, o objetivo é garantir que a matriz de bytes no fluxo atual possa acomodar esta operação de gravação.
Este método também é muito interessante. Se você achar que o BUF interno não pode suportar esta operação de gravação após o cálculo, o método de crescimento será chamado para expansão. O princípio da expansão da capacidade é semelhante ao do Arraylist, expandido para o dobro da capacidade original.
Além disso, o ByteArrayOutputStream também possui um método WritEto:
public sincronizado void writeto (outputStream Out) lança ioexception {out.write (buf, 0, contagem);}Escreva nossa matriz de bytes encapsulada internamente em um fluxo de saída.
Alguns dos métodos restantes também são muito usados:
Observe que, embora esses dois fluxos sejam chamados de "fluxos", eles essencialmente não alocam alguns recursos, como fluxos reais, por isso não precisamos chamar seu método próximo, e é inútil chamá -lo (o funcionário disse, não tem efeito).
Os casos de teste não serão liberados. Vou fazer upload de todos os casos de código usados neste artigo posteriormente. Você pode optar por baixá -los sozinho.
Para controlar o comprimento, o aprendizado restante será colocado no próximo artigo.
Todos os códigos, imagens e arquivos no artigo são armazenados na nuvem no meu github:
(https://github.com/singleyam/overview_java)
Você também pode optar por baixar localmente.
Resumir
O acima é o conteúdo inteiro deste artigo. Espero que o conteúdo deste artigo tenha certo valor de referência para o estudo ou trabalho de todos. Se você tiver alguma dúvida, pode deixar uma mensagem para se comunicar. Obrigado pelo seu apoio ao wulin.com.