字节输出流和字节输入流

字节输出流和输入流

    • 1. 字节输出流
    • 2. 字节输入流

之前介绍的File文件操作类可以关联本地文件系统,实现文件的创建,删除和查询一些文件信息。但是File类不支持对文件内容进行操作,如果要处理文件内容,必须通过流的方式。

流分为输入流和输出流。输入流:数据流入程序;输出流:数据从程序流出。

java.io包中,流分为两种:字节流和字符流。

  1. 字节流:InputStream, OutputStream
  2. 字符流:Reader, Writer

不管使用的字节流还是字符流,其基本的操作流程几乎是一样的,以文件操作为例。

  1. 根据文件路径创建File对象;
  2. 根据字节流或字符流的子类实例化父类对象;
  3. 进行数据的读取或者写入操作;
  4. 关闭流(close()).

对于I/O操作属于资源处理,所有的资源处理操作(I/O操作、数据库操作、网络)最后必须要进行关闭。

这篇博客主要介绍字节的输入流和输出流

1. 字节输出流

如果想要通过程序将数据流出,则通过java.io.OutputStream
OutputStream类的结构:

public abstract class OutputStream implements Closeable, Flushable

由以上可知OutputStream是一个抽象类,所以无法直接实例化,必须通过子类实例化。
OutputStream子类如下:
在这里插入图片描述
文件输出流是用于将数据写入到输出流File中。

FileOutputStream中的构造方法如下:

  1. 创建输出流写入指定的File对象(覆盖):public FileOutputStream(String name) throws FileNotFoundException
  2. 创建输出流写入指定的File对象(追加):public FileOutputStream(String name, boolean append) throws FileNotFoundException

举例:实现文件内容的输出

public class TestStream {
    public static void main(String[] args) {
        String directory = "D:"+File.separator+"test1"+File.separator
                +"java"+File.separator+"hh.txt";
        //File和本地文件系统相关联
        File file = new File(directory);

        OutputStream out = null;
        try {
           
            out = new FileOutputStream(file);
            //写入一个字节
            out.write('b');
            out.write('\n');
            //写入字节数组
            out.write("hello world!!!\n".getBytes());
            //写入指定字节长度
            String str = "I love programming!!!";
            byte[] bytes = str.getBytes();
            out.write(bytes, 0 , 8);
            //刷新此字节流并强制缓冲区的输出字节被写出
            out.flush();
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }finally{
            if(out!=null){
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

在上面中,需要注意两点

  1. 输出流文件如果存在,那么会自动创建;
  2. 在FileOutputStream中的构造方法中,append默认是false

另外,以上用到了OutputStream的三个方法

  1. 将给定的字节数组内容全部输出:public void write(byte b[]) throws IOException
  2. 将部分字节内容输出:public void write(byte b[], int off, int len) throws IOException
  3. 输出单个字节:public abstract void write(int b) throws IOException;

通过上述代码发现,手动关闭流对象过于繁琐,所以JDK1.7后提供了自动关闭功能。这是因为OutputStream实现了AutoCloseable接口,Java虚拟机会帮我们自动关闭

举例:实现自动关闭

public class TestStream {
    public static void main(String[] args) {
        String directory = "D:"+File.separator+"test1"+File.separator
                +"java"+File.separator+"hh.txt";
        //File和本地文件系统相关联
        File file = new File(directory);

        //在括号内实例化对象,最后会实现自动关闭
        try ( OutputStream out = new FileOutputStream(file);){
            out.write(49);   //字符1
            out.write("\n".getBytes());

            String str = "helloworld";
            byte[] buff = str.getBytes();
            out.write(buff, 0,5);

            out.flush();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
}

2. 字节输入流

OutputStream是将数据输出到文件中,那么使用InputStream就是读取文件的内容
InputStream的定义如下:

public abstract class InputStream implements Closeable 

因为也是抽象类,所以必须使用子类FileInputStream,从文件系统中的文件获取输入字节流。

InputStream中提供了如下方法

  1. public int read(byte b[])throws IOException:读取数据到字节数组中,返回数据的读取个数。
  2. public int read(byte b[], int off, int len)throws IOException:读取传递数组部分内容。
  3. public abstract int read()throws IOException:读取单个字节。

这三种,当读取没有数据时返回-1.

举例:读取文件内容

public class TestStream {
    public static void main(String[] args) {
        String directory = "D:"+File.separator+"test1"+File.separator
                +"java"+File.separator+"hh.txt";
        //File和本地文件系统相关联
        File file = new File(directory);
        if(file.exists()&&file.isFile()){
            try (InputStream in = new FileInputStream(file);){

                byte[] data = new byte[7];
                int len = in.read(data);
                System.out.println("读取数据的长度:"+len);
                System.out.println("读取的数据:"+new String(data));
                //读取单个字节
                System.out.println(in.read());
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else{
            throw new RuntimeException("文件不存在或者文件是个目录");
        }
    }
}

但以上中产生了一个问题:不能确定是否读完,只能一直读。

举例:采用循环的方式

public class TestStream {
    public static void main(String[] args) {
        String directory = "D:"+File.separator+"test1"+File.separator
                +"java"+File.separator+"hh.txt";
        //File和本地文件系统相关联
        File file = new File(directory);
        if(file.exists()&&file.isFile()){
            try (InputStream in = new FileInputStream(file);){

                int d = -1;
                while((d = in.read())!= -1){
                    System.out.print((char)d);  //读出来的为字节,强制转换
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else{
            throw new RuntimeException("文件不存在或者文件是个目录");
        }
    }
}

一个一个字节读取,效率太慢,所以采用缓冲区的读取方式。

举例:缓冲区方式读取

public class TestStream {
    public static void main(String[] args) {
        String directory = "D:"+File.separator+"test1"+File.separator
                +"java"+File.separator+"hh.txt";
        //File和本地文件系统相关联
        File file = new File(directory);
        if(file.exists()&&file.isFile()){
            try (InputStream in = new FileInputStream(file);){
                byte[] buff = new byte[1024];
                int len = -1;
                while((len = in.read(buff))!=-1){
                    //有了偏移量,从0开始读取
                    System.out.print(new String(buff,0,len));
                }
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }else{
            throw new RuntimeException("文件不存在或者文件是个目录");
        }
    }
}

简单总结,以文件操作为例

1. 对于OutputStream:就是将数据流出,将数据写到文件里;常用的使用的方法就是write(byte b[])
2. 对于InputStream:就是读取文件中的数据,流入程序;常用的使用方法就是read(byte b[], int offset, int len)
3. 对于OutputStream来说,写入到文件中时,若文件没有,可以自动创建;而对于InputStream来说,文件必须存在

你可能感兴趣的:(Java)