本站消息

站长简介


前每日优鲜python全栈开发工程师,自媒体达人,逗比程序猿,钱少话少特宅,我的公众号:想吃麻辣香锅

  java大神匠心打造,零基础java开发工程师视频教程全套,基础+进阶+项目实战,包含课件和源码

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2021-05(17)

2021-06(35)

IO流介绍

发布于2021-06-08 12:17     阅读(160)     评论(0)     点赞(17)     收藏(4)


0

1

2

3

4

概述

我们在编程的过程中,往往忽略一些比较基础的类以及功能,在这些方法以及概念上的使用和理解很薄弱,并不熟悉,偶尔看到别人代码和理解的时候,觉得自己理解了,会用了,等到真正有需求的时候,有场景的时候,却又不会使用。其中就存在对I/O的理解和使用,下面介绍一下I/O

正文

javaI/O流是java提供的一套输入输出API,在大部分程序中都需要输入输出,常用的system.out.print就是在控制台上输出。

在学习I/O的时候,很多时候会给一些概念混淆,什么是输入和输出? 输入难道不是从程序中输入到别的地方吗?输出难道不是从源路径输出到程序中吗?读的时候难道不是获取文件的输出流吗?写的时候难道不是获取输入流吗?毕竟读文件的时候是从文件中读取。所以叫做输出流,写文件,既然是写文件,那就是写入,难道不是获取文件的输入流吗?我常常就存在这样的疑问?

为了消除这些疑问?我们今天尝试好好理解这些问题。

  1. I/O主要关注的是源文件的读取以及写入到目标媒介。这里的媒介可以是文件,管道以及socket
输出流
源文件
输入流
  1. 这里如何理解输入和输出?这里存在一个参照物,这个参照物就是 内 存 \color{red}{内存} ,对于 内 存 \color{red}{内存} 来说,往 内 存 \color{red}{内存} 中读取的流就叫做输入流,一般情况下这里的内存指代的是编写的程序。从 内 存 \color{red}{内存} 中往外面写就是输出流
  2. 比如读取一个文件,就是从磁盘往内存中读取,获取socket中的数据,就是从socket中往内存中读取。所以读取文件和获取socket中的数据,都必须得到这个文件的输入流和socket的输入流
    InputStream inputStream = new FileInputStream(File file)
    InputStream inputStream = socket.getInputStream()
    如果往磁盘上的文件里写内容和往其他socket中发送消息,就是从内存(程序中)将数据写出,那么就是输出流,如写一个文件,就是从程序内存中写出;往socket中写入数据,就是从内存中往socket中写入,
    FileOutStream outputStream = new FileOutputStream(File file)
    OutputStream outputStream = socket.getOutputStream()
  3. 流是JAVA的一个核心概念,可以理解成一个水流。你可以在这个水流中取水,也可以放入水到这个水流中。

流的分类

JAVA中一共存在四种基础的流类,分别是InputStreamOutputStream以及ReaderWriter,在这基础上,分为多个子类,文件的FileInputStreamFileOutputStream数组的ByteArraryInputStreamByteArrayOutputStream

  • 流的方向分类: 输入流和输出流
  • 流的类型: 字节流和字符流
  • 流的功能: 节点流和处理流(包装流)

字节流

FileInputStream与FileOutputStream

FileInputStreamFileOutputStream是字节输入输出流的子类,主要针对文件的操作,包括文件的读写

FileInputStream
 class FileInputStream extends InputStream 

FileInputSteam是用于操作文件的输入流,inputStream是输入流,表示将外部数据读取到内存中。也就是说如果需要读取文件的内容,那么就必须获取这个文件的输入流。
InputStream inputStream= new FileInputStream(String path)或者InputStream inputStream= newFileInputStream(File file)都是创建一个文件输入流.
inputStream.read():使用read方法可以读取到一个字节的文件数据,该方法返回的是读取到字节,如果读到文件的末尾,那么就返回-1;
inputStream.read(byte[] bytes):带字节数组的read方法表示将读取的数据放入字节数组中,返回的是读取到字节数组的个数,如果读到文件的末尾,那就返回-1;

  InputStream fileInputStream = new FileInputStream("D://student.txt");
  byte[] bytes = new byte[1024]int len;
  while ((len=fileInputStream.read(bytes))!=-1){
    String msg = new String(bytes,0,len);
     System.out.println(msg);
  }
FileOutputStream
FileOutputStream extends OutputStream

FileOutputStream是操作文件的输出流。也就是表示可以将内存的数据写出到指定文件中。
OutputStream outputStream = new FileOutputStream(String path)OutputStream outputStream = new FileOutputStream(File file)都可以创建一个文件输出流。这里的创建的文件可以存在也可以不存在。如果不存在自动创建。不会像创建文件的时候出现FileNotFoundException
outputStream.write(int b):使用write方法可以写入一个字节到文件中。
outputStream.write(bytes[] bytes):带byte数组的方法表示将bytes中的数据写出到数据。

   OutputStream fileOutputStream = new FileOutputStream("D://out.txt");
   fileOutputStream.write(int b);
   fileOutputStream.write("hello".getBytes());

运行完毕后,会在本机的D盘出现一个out.txt文件,里面的内容为hello,但是再次运行的时候,那么原来的数据将会被新的hello数据覆盖,而往往实际场景中都是向文件中追加,那么就需要在创建输出流FileOutput的时候指定覆盖
OutputStream fileOutputStream = new FileOutputStream("D://out.txt",ture)

字节流复制文件
InputStream inputStream = new FileInputStream("D://test04.avi");
OutputStream outputStream = new FileOutputStream("D://test04_copy.avi");
    byte[] bytes = new byte[1024];
    int len;
     while((len=inputStream.read(bytes))!=-1){
         outputStream.write(bytes,0,len);
     }
     if(inputStream!=null){
          inputStream.close();
      }
      if(outputStream!=null){
          outputStream.close();
      }

这就是利用了inputSreamoutputStream读取和写入按照字节,首先读取到字节数组中,然后将读取的字节数组写入到字节输出流中。但是有时候。我们按照字节读取会很慢,一个汉字根据编码的不同,出现不同个数的字节,这时候就需要字符来提高效率。

ObjectInputStream与ObjectOutputStream

ObjectInputStreamObjectOutputStream是对象字节输入输出流。当然也是字节InputStreamOutputStream的子类,在介绍这个类之前。 首先需要理解什么是序列化

  1. 在计算机的世界里面只有0和1这两个数字。所以将一个对象写入到内存或者在网络传输的时候,都必须将对象转成1和0的数字,比例将int 100保存到文件中,当读取文件的时候,不仅仅需要读到100这个数字,还需要将100这个数字的类型转成int,这样才算完整的对象。
  2. 一个对象,有许多属性,创建这个对象并且赋值的时候,将这个对象保存到文件中,然后需要的时候,在从文件中读取,这时候就需要用到ObjectInputStreamObjectOutputStream
  3. 创建一个对象
    public class SysUser implements Serializable {
        private String id;
        private String name;
        private Integer age;
    }
    
ObjectOutputStream
ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:\\user.dat"));
     SysUser sysUser = new SysUser();
     sysUser.setAge(10);
     sysUser.setId("1");
     sysUser.setName("HELLO");
     objectOutputStream.writeObject(sysUser);

创建一个文件输出流,实例化一个SysUser对象,然后将这个对象写入到文件user.dat中。运行程序后,可以D盘中发现一个user.dat文件。但是这里会出现一个错误
read "main" java.io.NotSerializableException: com.demo.nio.two.SysUser
意思是能够被写入文件的对象必须能够序列化 ,在JDK中如果需要序列化一个对象。只需要这个对象实现Serializable接口即可。不仅仅是这个对象需要,里面的属性类型也需要实现序列化,String默认实现了Serializable接口,以及8大数据类型中除了Boolean都已经实现了序列化接口。

ObjectInputStream

可以根据上面创建的对象文件,将原本存在文件中的SysUser对象输入到内存

 ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:\\user.dat"));
  SysUser o = (SysUser)objectInputStream.readObject();
  System.out.println(o);

在这里插入图片描述

PipeInputStream与PipeOutputStream

Pipe管道输入输出流用于线程之间的通信。B线程写数据到PipeOutputStream,另一个A线程读取B线程在Pipe管道中写数据

PipeOutputStream

定义一个线程B,在实例化的时候,给成员变量outputStream赋值

class ThreadB extends  Thread{
    private OutputStream outputStream;
    public ThreadB(OutputStream outputStream){
        this.outputStream = outputStream;
    }
    @Override
    public void  run(){
        try {
            //向管道中写入数字
            outputStream.write("1234".getBytes());
        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
PipeInputStream

定义一个线程A,实例化构造一个InputStream,并且给InputStream赋值

class  ThreadA extends  Thread{
    private InputStream inputStream;
    public ThreadA(InputStream inputStream){
        this.inputStream = inputStream;
    }
    @Override
    public void  run(){

        try {
            int len = inputStream.read();
            while(len>0){

                System.out.print((char)len);
                len=inputStream.read();
            }

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                inputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

通过connect方法将管道输入和管道输出流连接。

   public static void main(String[] args) {
        //定义一个管道输入流
        PipedInputStream inputStream = new PipedInputStream();
        //定义一个管道输出流
        PipedOutputStream outputStream = new PipedOutputStream();
        try {
            //管道输入连接输出流
            inputStream.connect(outputStream);
            new ThreadA(inputStream).start();
            new ThreadB(outputStream).start();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

字符流

FileReader与FileWriter
FileReader

读取文件按照一个字节读取,如果文件比较大,且文本内容比较多,尤其是遇到中文的情况下,使用字节流读取或者写入的时候,往往会出现乱码的情况,因为不同的编码 中文的字节数是不一样的。所以就需要字符流。因为无论在什么编码下一个中文就是一个字符。
FileReader就是文件输入流。按照字符读取文件内容FileReader reader = new FileReader(File file)或者FileReader reader = new FileReader(String path)都是创建一个文件字符输入流

  • read方法是读取一个字符,返回的是读取的字符
  • read(char[] char)是将读取的字符放入字符数组中,返回的是读取字符的个数,如果返回-1,则表示读到文件的末尾。
    public static void main(String[] args) {
        try {
            FileReader reader = new FileReader("D:\\简历\\2020.04.08简历\\简历\\舆情监测\\【文员—舆情监测专员(新闻监测专员)_南京】蒋卉峰 1年以内.pdf");
            int len;
            char[] chars = new char[1024];
            while ((len=reader.read(chars))!=-1){
                System.out.println(new String(chars,0,len));
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

这里需要注意的是,字符流在读取二进制数据的时候,往往会出现乱码,且候在复制操作的时候,复制文件会出现损坏,因为二进制文件在电脑的本质来说是字节,所以音频,视频,pdf等都必须使用字节流读取。

FileWriter

FileWriter是文件输出流,复制写入输入到文件中。通过FileWriter writer = new FileWriter(File file)或者FileWriter writer = new FileWriter(String filename)获取写入文件的FileWriter实例对象

  • write(char c)写入一个字符到文件中
  • write(String s)写入字符串到文件
  • write(char[] char)写入字符数组到文件中
    这些方法都没有返回值
    public static void main(String[] args) throws IOException {

        FileWriter fileWriter = new FileWriter("D:\\Hello.txt");
        fileWriter.write("HELLO world,字符输出流");
        
    }

值得注意的是,在字符输出流写数据到文件中。如果不执行flush方法或者close方法 ,数据是无法存储在文件中,必须之星flush方法和close方法。与字节输出流相同的是,文件字符流在写入文件的时候,采取的默认的也是覆盖,后输出的内容会覆盖原本文件内容,需要再构造的时候传入true来确保文件的追加。
FileWriter fileWriter = new FileWriter("D:\\Hello.txt",true);

使用字符流复制文件
 FileWriter fileWriter = new FileWriter("D:\\Hello_copy.txt");
       FileReader fileReader = new FileReader("D:\\Hello.txt");
       char[] chars = new char[1025];
       int len;
       while ((len=fileReader.read(chars))!=-1){
           fileWriter.write(chars,0,len);
       }
        fileWriter.close();
       fileWriter.close();

包装流

包装流是在字节或者字符流的基础上,通过装饰器模式,在原本的功能上面添加新的功能,以及对原功能的增加,最常用的字符包装流 在原本按照字符读取扩展了按照一行读取

BufferedReader与BufferedWriter
BufferedReader

字符读取包装流。在原来Reader的基础上加强了一些功能。比如按照一个字符读取,扩展了按照一行读取。
通过BufferedReader bufferedReader = new BufferedReader(Reader reader)实例化。传入一个Reader。如FileReader

  • readLine方法按照一行读取。返回的是读取的数据,如果返回null则表示读取到文件的末尾
   BufferedReader reader = new BufferedReader(new FileReader("D://Hello.txt"));
        String msg;
        while ((msg=reader.readLine())!=null){
            System.out.println(msg);
        }
BufferedWriter

Writer输出流的包装。
通过BufferedWriter writer = new BufferedWriter(new FileWriter("D:\\Hello.txt",true));构造一个字符包装流的对象

  • newLine方法是向文件写入空的一行
    其他的方法与Writer大致一样
BufferedInputStream与BufferedOutputStream

有字符包装流,当然也有字节包装流。在读取字节的时候,也可以通过字节包装流读写数据

BufferedInputStream

BufferedInputStream bufferedInputStream = new BufferedInputStream(InputStream inputStream);构建字节输入包装流

BufferedOutputStream

BufferedOutputStream bufferedOutputStream = new BufferedOutputStream(OutputStream outputStream);构建字节输出包装流

转换流

既然有字节流和字符流。那么能够能否将字节流转换成字符流。或者字符流转成字节流,前者是可以的 ,但是后者不行。我们可以将字节流转成字符流,但是不可以将字符流

InputStreamReader与OutputStreamWriter
InputStreamReader

InputStreamReader inputStreamReader = new InputStreamReader(InputStream inputStream)通过此构造方法 将字节输入流转成字符输入流

OutputStreamWriter

OutputStreamWriteroutStreamReader = new OutputStreamWriter(OutputStream outputStream)通过此构造方法 将字节输出流转成字符输出流

IO流大家族

IO大家族


站长简介:前每日优鲜python全栈开发工程师,自媒体达人,逗比程序猿,钱少话少特宅,关注我,做朋友, 我们一起谈人生和理想吧!我的公众号:想吃麻辣香锅

关注公众号回复java,免费领取 免费领取java大神匠心打造,零基础java开发工程师视频教程全套,基础+进阶+项目实战,包含课件和源码

关注公众号回复java充值+你的账号,免费为您充值1000积分

0

1

2

3

4

5

6

7



所属网站分类: 技术文章 > 博客

作者:我爱编程

链接:http://www.javaheidong.com/blog/article/219613/ff3d9287bcb3de9ea068/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

17 0
收藏该文
已收藏

评论内容:(最多支持255个字符)