Prefacio
En el artículo anterior, presentamos el tipo de archivo de archivos de disco abstracto. Solo se usa para describir de manera abstracta un archivo o directorio de disco, pero no tiene la capacidad de acceder y modificar el contenido de un archivo.
Java IO Stream es un diseño utilizado para leer y escribir el contenido del archivo. Puede completar la transferencia de datos del contenido del archivo de disco de salida a la memoria o los datos de memoria de salida a los archivos de disco.
El diseño de Java IO Streams no es perfecto. Ha diseñado una gran cantidad de clases, lo que ha aumentado nuestra comprensión de las transmisiones de IO, pero solo hay dos categorías principales: una es Byte Streams para archivos binarios, y el otro son transmisiones de caracteres para archivos de texto. En este artículo, primero aprenderemos los principios y los escenarios de uso de los tipos relacionados de transmisiones de bytes. Los tipos de transmisión específicos involucrados principalmente son los siguientes:
Transmisión de transmisión de byte de clase base/outputStream
InputStream y OutputStream son las clases base para leer transmisiones de bytes y escribir transmisiones de bytes respectivamente. Todas las corrientes relacionadas con los bytes deben heredar de cualquiera de ellas. Como clase abstracta, también definen las operaciones de lectura y escritura más básicas. Echemos un vistazo:
Tome InputStream como ejemplo:
public abstract int read () lanza IOException;
Este es un método abstracto y no proporciona una implementación predeterminada, que requiere que se deben implementar subclases. El propósito de este método es devolver el siguiente byte del archivo actual para usted.
Por supuesto, también encontrará que el valor de retorno de este método se recibe utilizando el tipo entero "int", entonces, ¿por qué no usar "byte"?
En primer lugar, el valor devuelto por el método de lectura debe ser un binario de ocho bits, y el intervalo de valor que puede ser tomado por un binario de ocho bits es: "0000 0000, 1111 1111", es decir, el rango [-128, 127].
El método de lectura también especifica que cuando el archivo se lee al final, es decir, el archivo no tiene el siguiente byte para leer, el valor -1 se devolverá. Entonces, si Byte se usa como tipo de valor de retorno, entonces cuando el método devuelve un -1, ¿deberíamos determinar si este es el contenido de datos en el archivo o el final de la transmisión?
El tipo int ocupa cuatro bytes, y los tres bytes en el bit alto son todos 0. Solo usamos su byte de bits más bajo. Al encontrar el final del indicador de la corriente, devuelve -1 (32 1s) representados por cuatro bytes, que es naturalmente diferente del valor -1 (24 0 + 8 1s) que representa los datos.
El siguiente es también un método de lectura, pero InputStream proporciona una implementación predeterminada:
public int read (byte b []) lanza ioexception {return read (b, 0, b.length);} public int read (byte b [], int off, int len) lanza ioexception {// para no hacer la longitud demasiado larga, puede ver el código fuente JDK por usted mismo}Estos dos métodos son esencialmente los mismos. El primer método es una forma especial del segundo método, que permite pasar una matriz de bytes y requiere que el programa llene los bytes leídos en el archivo a partir de la posición del índice de matriz 0 para llenar el número de bytes en la longitud de la matriz.
El segundo método es un poco más amplio, lo que le permite especificar la posición inicial y el número total de bytes.
Existen varios otros métodos en InputStream, que básicamente no se implementan en detalle. Echemos un vistazo brevemente.
El método Mark marcará un indicador en la posición de lectura del flujo actual, y el método de reinicio restablecerá el puntero de lectura al indicador.
De hecho, es imposible reiniciar la lectura para la lectura de archivos, pero generalmente todos los bytes entre la posición del indicador y el punto de reinicio se guardan temporalmente. Cuando se llama al método de reinicio, en realidad se repite la lectura del conjunto de bytes temporal guardado, por lo que readLimit se usa para limitar la capacidad máxima de caché.
El método de MarkSupported se utiliza para determinar si la secuencia actual admite esta operación de lectura "Fallback".
OutputStream y InputStream son similares, excepto que uno está escrito y el otro se lee. No lo repetiremos aquí.
File Byte Stream FileInput/OutputStream
Todavía nos estamos centrando en FileInputStream, y FileOutputStream es similar.
Primero, FileInputStream tiene los siguientes constructores para instanciar un objeto:
Public FileInputStream (nombre de cadena) lanza FileNotFoundException {this (name! = NULL? Nuevo archivo (nombre): null);} public FileInputStream (archivo de archivo) lanza FileNotFoundException {String name = (file! = NULL? File.getPath (): null); SecurityManager Security = System.getSecurityManager (); if (Security! = Null) {Security.Checkread (nombre); } if (name == NULL) {tire nuevo nullPointerException (); } if (file.isinvalid ()) {lanzar nueva filenotfoundException ("ruta de archivo no válido"); } fd = new FileDescriptor (); fd.attach (esto); ruta = nombre; Abrir (nombre);}Estos dos constructores son esencialmente los mismos, el primero es la forma especial de la segunda. De hecho, no mire el último método, la mayoría de los cuales solo están haciendo verificación de seguridad. El núcleo es un método abierto, que se utiliza para abrir un archivo.
Principalmente estos dos constructores, si el archivo no existe o la ruta y el nombre del archivo son ilegales, se lanzará una FileNotFoundException.
Recuerde que dijimos que hay un método abstracto leído en la clase base InputStream que requiere que se implementen todas las subclases, y FileInputStream se implementa utilizando un método local:
public int read () lanza ioexception {return read0 ();} private int int read0 () lanza ioexception;No tenemos forma de explorar la implementación específica de Read0 por el momento, pero debe estar claro que la función de este método de lectura se utiliza para devolver el siguiente byte en la transmisión y return -1. Significa que se lee al final del archivo y no hay bytes para leer.
Además, hay algunos otros métodos relacionados con la lectura en FileInputStream, pero la mayoría de ellos se implementan utilizando métodos locales. Echemos un vistazo aquí:
Los métodos internos de FileInputStream son básicamente así, y hay algunos avanzados y complejos que no podemos usar por el momento. Lo aprenderemos más tarde. Echemos un vistazo a un breve ejemplo de lectura de archivos:
public static void main (String [] args) lanza IOException {FileInputStream input = new FileInputStream ("c: //users//yanga//desktop//test.txt"); byte [] buffer = new Byte [1024]; int len = input.read (buffer); Cadena str = new String (buffer); System.out.println (str); System.out.println (len); input.close ();}El resultado de salida es muy simple. Imprimirá el contenido en nuestro archivo de prueba y se lee el número real de bytes, pero los estudiantes cuidadosos descubrirán, ¿cómo puede asegurarse de que el contenido en el archivo de prueba no exceda de 1024 bytes?
Para leer completamente el contenido del archivo, una solución es definir el búfer lo suficientemente grande como para esperar almacenar todo el contenido del archivo tanto como sea posible.
Obviamente, este método es indeseable porque es imposible para nosotros darnos cuenta del tamaño real del archivo a leer. Es una solución muy mala simplemente crear una matriz de bytes de gran tamaño.
La segunda forma es utilizar nuestra corriente de matriz de bytes dinámico, que puede ajustar dinámicamente el tamaño de la matriz de bytes interna para garantizar la capacidad adecuada, que introduciremos en detalle más adelante.
Con respecto a FileOutputStream, una cosa más para enfatizar es su constructor, que tiene los siguientes dos constructores:
Public FileOutputStream (nombre de cadena, boolean append) Public FileOutputStream (archivo de archivo, boolean append)
El parámetro agregado indica si la operación de escritura de esta transmisión está sobrescribida o adjunta, verdadera medias adjuntas, falsas medias sobrescribidas.
BytearrayInput/outputStream
La llamada "transmisión de matriz de bytes" es una transmisión que funciona alrededor de una matriz de bytes. No lee y escribe transmisiones a archivos como otras transmisiones.
Aunque la transmisión de la matriz de bytes no es una transmisión basada en archivos, sigue siendo una secuencia muy importante, porque la matriz de bytes encapsulada en el interior no es fija, sino dinámicamente extensible, y a menudo se basa en ciertos escenarios, lo cual es muy adecuado.
ByteArrayInputStream es un flujo de matrices de lectura de bytes que pueden ser instanciadas por el siguiente constructor:
byte buf []; protegido int POS; protegido int count; public bytearrayInputStream (byte buf []) {this.buf = buf; this.pos = 0; this.count = buf.length;} public bytearrayInputStream (byte buf [], int offset, int longitud)BUF es una matriz de bytes encapsulada dentro de bytearrayInputStream. Todas las operaciones de lectura de BytearrayInputStream giran en torno a él.
Por lo tanto, al instancias de un objeto bytearrayInputStream, se pasa al menos una matriz de byte objetivo.
El atributo POS se utiliza para registrar la posición de la lectura de la transmisión actual, y contar registra la última posición del último índice de bytes válido de la matriz de bytes de destino.
Después de comprender esto, no es difícil leer varias formas de leerlo:
// leer el próximo byte público sincronizado int read () {return (pos <count)? (buf [pos ++] & 0xff): -1;} // Leer bytes de len y ponerlos en la matriz de byte B public sincronizado int (byte b [], int off, int len) {// mismo, el cuerpo del método es más largo, todos verifican su propio jdk}}}}Además, BytearrayInputStream también implementa la operación de "Repetir lectura" de manera muy simple.
Public void Mark (int readeheadlimit) {mark = pos;} public sincronizado void reset () {pos = mark;}Debido a que ByteArrayInputStream se basa en matrices de bytes, todas las operaciones de lectura repetidas son más fáciles de implementar, y es suficiente para implementar en base a los índices.
BytearRayOutputStream es una secuencia de matriz de bytes escrita. Muchas implementaciones todavía tienen sus propias características. Echemos un vistazo juntos.
Primero, se requieren estas dos propiedades:
byte buf []; // El recuento aquí representa el número de bytes válidos en BUF protegido int count;
Constructor:
public byteArRaReOutputStream () {this (32);} public bytearRayOutputStream (int size) {if (size <0) {Throw New IlegalArGumentException ("Tamaño inicial negativo:"+ tamaño); } buf = new Byte [size];}La tarea central del constructor es inicializar el BUF de matriz de bytes interno, lo que le permite pasar de tamaño para limitar explícitamente el tamaño de la matriz de bytes inicializado, de lo contrario, la longitud predeterminada será 32.
Escriba contenido a bytearRayOutputStream desde el exterior:
Public Syncronized void Write (int b) {EnsureCapacity (recuento + 1); buf [count] = (byte) b; count + = 1;} public sincronizado void write (byte b [], int off, int len) {if ((off <0) || (Off> b.length) || (len <0) || ((Off + len) - b.length> 0)) {tirar nueva indexOutOfBoundsexception (); } EnsureCapacity (Count + Len); System.ArrayCopy (B, Off, BUF, Count, Len); count += len;}Al ver eso, el primer paso de todas las operaciones de escritura es llamar al método Ensurecapacity, el propósito es garantizar que la matriz de bytes en la secuencia actual pueda acomodar esta operación de escritura.
Este método también es muy interesante. Si encuentra que el BUF interno no puede soportar esta operación de escritura después del cálculo, se solicitará el método de crecimiento para la expansión. El principio de expansión de la capacidad es similar al de ArrayList, expandido al doble de la capacidad original.
Además, BytearRayOutputStream también tiene un método WriteTo:
Public sincronizado void writeTo (outputStream out) lanza ioexception {out.write (buf, 0, count);}Escriba nuestra matriz de bytes encapsulada internamente en una secuencia de salida.
Algunos de los métodos restantes también se usan muy comúnmente:
Tenga en cuenta que aunque estas dos transmisiones se llaman "transmisiones", esencialmente no asignan algunos recursos como transmisiones reales, por lo que no necesitamos llamar a su método cercano, y es inútil llamarlo (el funcionario dijo, no tiene ningún efecto).
Los casos de prueba no serán liberados. Cargaré todos los casos de código utilizados en este artículo más adelante. Puede elegir descargarlos usted mismo.
Para controlar la longitud, el aprendizaje restante se colocará en el próximo artículo.
Todos los códigos, imágenes y archivos en el artículo se almacenan en la nube en mi github:
(https://github.com/singleyam/overview_java)
También puede elegir descargar localmente.
Resumir
Lo anterior es todo el contenido de este artículo. Espero que el contenido de este artículo tenga cierto valor de referencia para el estudio o el trabajo de todos. Si tiene alguna pregunta, puede dejar un mensaje para comunicarse. Gracias por su apoyo a Wulin.com.