Chp19-IO1-字节流

Chp19-IO1-字节流

IO的概念

使数据在JVM和本地磁盘之间传输的技术

I: input 输入

O: output 输出

流的概念

为数据传输做支持, 相当于管道

流的分类

  1. 从传输方向上看:

    • 输入流: 从本地磁盘传输到JVM
    • 输出流: 从JVM传输到本地磁盘
  2. 从传输单位上看:

    • 字节流: 以字节为单位进行数据传输, 可以传输任意类型的文件, 如文本|图片|视频|音频等
    • 字符流: 以字符为单位进行数据传输, 只能传输文本类型的文件, 如.txt|.java|.html等
  3. 从功能上看:

    • 节点流: 具有实际传输意义的流
    • 过滤流: 没有实际传输功能, 作用为给节点流增强传输能力或者添加附加功能

字节输入流

  • InputStream: 抽象父类
  • FileInputStream: 字节输入节点流. 是InputStream的子类

创建

FileInputStream fis=new FileInputStream("被读取的文件路径");
  • 绝对路径: 以电脑盘符为基点的完整路径

    FileInputStream fis = new FileInputStream("D:\\206\\java基础\\testFile.txt");
    FileInputStream fis = new FileInputStream("D:/206/java基础/testFile.txt");
    

    \\: 通过一个转义字符对\进行语法转义,使其变为普通的路径分隔符

  • 相对路径: 以当前项目路径为基点的路径

     		//绝对路径
            FileInputStream fis = new FileInputStream("C:\\Users\\Administrator\\IdeaProjects\\Chp19_206\\file\\a.txt");
            //相对路径
            FileInputStream fis2 = new FileInputStream("file\\a.txt");
            FileInputStream fis3 = new FileInputStream("file/a.txt");
    

    实际开发中, 不约束文件存储位置的绝对路径使用更多

    学习过程中, 建议使用查看操作更便捷的相对路径

  • 要求:

    1. 路径必须截止至文件
    2. 文件必须已经存在
    3. 构造会抛出java.io.FileNotFoundException文件路径找不到的非运行时异常, 必须对其作出处理

常用方法

  1. void close(): 关闭流链接,释放相关资源. 所有流都具备该方法
  2. int read(): 读取一个字节并返回, 读取到达末尾, 返回-1
  3. int read(byte[] b): 读取数组长度个字节存入到数组b中, 返回实际读取个数, 读取到达末尾, 返回-1
public static void main(String[] args)throws Exception {		
//字节输入节点流-读取当前项目下的file/a.txt中的"abcdefg"
        FileInputStream fis = new FileInputStream("file/a.txt");

        //利用read()读取文件所有内容
        while (true) {//假设一直读取
            //接收本次读取结果
            int n = fis.read();
            //判断读取是否到达末尾
            if (n == -1) {
                System.out.println("读取结束");
                break;
            }
            //正常操作当前读取内容
            System.out.println((char) n+"  ");
        }//a b c d e f g    
}
		//利用read(byte[] b)读取文件所有内容
        while (true) {
            //创建长度为5的数组
            byte[] bs = new byte[5];
            //尝试读取5个字节到数组中,并接收返回值
            int n = fis.read(bs);
            //判断读取是否到达末尾
            if (n == -1) {
                System.out.println("读取结束!");
                break;
            }
            //遍历数组, 操作本次读取的内容
            System.out.println("本次读取长度: "+n);
            for (byte b : bs) {
                System.out.print(b+"  ");
            }
        }

字节输出流

  • OutputStream: 抽象父类
  • FileOutputStream: 字节输出节点流, OutputStream的子类

创建

FileOutputStream fos=new FileOutputStream("接收数据的文件路径",true|false);
  • 对路径的要求与输入流一致
  • true|false: 设置数据在文件中的存储方式
    • true: 追加
    • false: 覆盖, 默认为覆盖
  • 当文件不存在时,会自动创建
    • 无法创建文件夹

常用方法

  1. flush(): 强制刷新缓冲区. 所有输出流都具备该方法
  2. write(int n): 一次写入一个字节
  3. write(byte[] b): 一次写入一个数组的数据
public static void main(String[] args)throws Exception {
//创建一个字节输出节点流- 向file/b.txt中写入内容-数据追加
        FileOutputStream fos = new FileOutputStream("file/b.txt",true);

        //写入ABCD
        fos.write(65);
        fos.write(66);
        fos.write('C');
        fos.write('D');

        //以数组的方式写入ABCD
        String str = "ABCD";
        byte[] bs = str.getBytes();
        fos.write(bs);

       /* String str = "abcdefg";
        fos.write(str.getBytes());
*/
        //关流
        fos.close();
        System.out.println("操作正常!");
}

标准异常处理-自动关流

try(
	需要自动关流的内容创建代码
){
    其他的操作代码
}catch(...){
    ...
}...
  • 原理: JDK7.0之后, 所有流都默认实现了AutoCloseable接口, 该接口中提供了自动关联所需的close()
try (//创建一个字节输入节点流- file/a.txt
                FileInputStream  fis = new FileInputStream("file/c.txt");
                ){
            //循环读取文件所有内容
            while (true) {
                //接收本次读取结果
                int n = fis.read();
                //判断读取是否到达末尾
                if (n == -1) {
                    break;
                }
                System.out.println(n + "  ");
            }
        } catch (FileNotFoundException e) {
            System.out.println("文件路径不正确");
        } catch (IOException e) {
            System.out.println("读取失败!");
        } catch (Exception e) {
            System.out.println("发生异常!");
            e.printStackTrace();
        }

文件复制

原理

利用JVM, 先将文件A中的内容读取到JVM中, 再将读取内容通过JVM写入到文件B中

先读后写

package com.by.test.copy;

import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

public class Test_copy {
    public static void main(String[] args) {
        copy1();
    }
    //一次复制一个字节
    public static void copy1(){
        try(
                //先建输出流- 被写入的文件路径
                FileOutputStream fos=new FileOutputStream("file/c.txt");
                //再建输入流 - 被复制的文件路径
                FileInputStream fis=new FileInputStream("file/b.txt")
                ){
            //先读
            while (true) {
                //接收本次读取字节
                int n = fis.read();
                if (n == -1) {
                    break;
                }
                //后写: 将本次读取的字节写入到目标文件
                fos.write(n);
            }

            System.out.println("复制完成!");
        }catch (FileNotFoundException e) {
            System.out.println("文件路径不正确");
        } catch (IOException e) {
            System.out.println("读取失败!");
        } catch (Exception e) {
            System.out.println("发生异常!");
            e.printStackTrace();
        }
    }


//一次复制一个数组
    public static void copy2(){
        try(
                //先建输出流- 被写入的文件路径
                FileOutputStream fos=new FileOutputStream("file/d.txt");
                //再建输入流 - 被复制的文件路径
                FileInputStream fis=new FileInputStream("file/b.txt")
        ){
            //先读
            while (true) {
                byte[] bs = new byte[1024];
                //读取一个数组的数据到数组中,并接收返回值
                int n = fis.read(bs);
                if (n == -1) {
                    break;
                }
                //将本次读取的数组写入到目标文件
                fos.write(bs);
            }
            System.out.println("复制完成!");
        }catch (FileNotFoundException e) {
            System.out.println("文件路径不正确");
        } catch (IOException e) {
            System.out.println("读写失败!");
        } catch (Exception e) {
            System.out.println("发生异常!");
            e.printStackTrace();
        }
    }
}

字节复制的优势: 不会浪费空间

数组复制的优势: 效率较高

缓冲过滤流

提高节点流的传输能力

内置一个数据缓冲区, 当进行文件复制时, 读取到的内容会暂时存放在缓冲区中, 当缓冲区刷新时再将内容直接写入到目标文件, 通过降低传输频率来提高传输效率

创建

  • BufferedInputStream: 字节输入缓冲过滤流
  • BufferedOutputStream: : 字节输出缓冲过滤 流

必须基于节点流对象

BufferedInputStream bis=new BufferedInputStream(fis对象);
BufferedOutputStream bos=new BufferedOutputStream(fos对象);
public static void copy3(){
        long l1 = System.nanoTime();

        try(
                //先建节点流对象
                //先建输出流- 被写入的文件路径
                FileOutputStream fos=new FileOutputStream("D:\\206\\java基础\\笔记\\206一阶段笔记4.md");
                //再建输入流 - 被复制的文件路径
                FileInputStream fis=new FileInputStream("D:\\206\\java基础\\笔记\\206一阶段笔记.md");
                //再添加过滤流
                BufferedOutputStream bos=new BufferedOutputStream(fos);
                BufferedInputStream bis=new BufferedInputStream(fis)
                ){
            while (true) {
                int n = bis.read();
                if (n == -1) {
                    break;  
                }
                //将本次读取写入到目标文件
                bos.write(n);
            }
            System.out.println("复制成功!");

        }catch (FileNotFoundException e) {
            System.out.println("文件路径不正确");
        } catch (IOException e) {
            System.out.println("读写失败!");
        } catch (Exception e) {
            System.out.println("发生异常!");
            e.printStackTrace();
        }

        long l2 = System.nanoTime();
        System.out.println("字节复制+缓冲过滤流: "+(l2-l1)/1E9);
    }

实际开发中, 字节复制+缓冲过滤流的执行效率已经足以满足开发需求

使用

  1. 当对同一文件同时进行先写后读的操作时, 必须在写入完成之后提前刷新缓冲区, 将内容写入到文件才能正常读取

    • 刷新缓冲区的方式:

      bos.close(): 提前关流, 关流之前会自动刷新缓冲区
      bos.flush(): 直接强刷缓冲区 (推荐)
      
  2. 大部分的流为了提高数据传输效率, 底层都嵌套着过滤流, 使用数据缓冲区

package com.by.test;

import java.io.*;

public class Test_buffered {
    public static void main(String[] args) {
        //先往file/a.txt中写入26个小写字母, 再读取输出
        try(
                //输出-写入
                BufferedOutputStream bos=new Buffere dOutputStream(new FileOutputStream("file/a.txt"));
                //输入-读取
                BufferedInputStream bis=new BufferedInputStream(new FileInputStream("file/a.txt"))
                ){
            //先写入26个小写字母
            for (int i = 97; i <=122 ; i++) {
                bos.write(i);
            }
            System.out.println("写入成功!");
            //手动关流
           // bos.close();
            //强制刷新缓冲区
            bos.flush();
            //再对文件中的内容进行读取
            while (true) {
                int n = bis.read();
                if (n == -1) {
                    break;
                }
                System.out.print((char) n+" ");
            }
            System.out.println("读取成功!");

           // bos.write(65);

        }catch (FileNotFoundException e) {
            System.out.println("文件路径不正确");
        } catch (IOException e) {
            System.out.println("读写失败!");
        } catch (Exception e) {
            System.out.println("发生异常!");
            e.printStackTrace();
        }
    }
}

对象过滤流

给节点流添加附加功能

  1. 读写基本类型
  2. 读写引用类型
  • ObjectInputStream: 对象输入过滤流-ois
  • ObjectOutputStream: 对象输出过滤流-oos

创建

ObjectInputStream ois=new ObjectInputStream(fis对象);
ObjectOutputStream oos=new ObjectOutputStream(fos对象);

读写基本类型

xxx ois.readXxx(): 读取基本类型
void  oos.writeXxx(xxx): 写入基本类型
注意: xxx对应的为基本类型

使用

  1. 先写后读时需要在写入完成后强刷缓冲区

  2. 为了确保数据传输的安全性, 对象过滤流会在数据写入时对其按照魔数机制进行加密, 在读取时再进行解密.

package com.by.test;

import java.io.*;

public class Test_object {
    public static void main(String[] args) {
        //先往file/a.txt中写入一个小数, 再读取输出
        try(
              FileOutputStream fos=new FileOutputStream("file/a.txt");
              FileInputStream fis=new FileInputStream("file/a.txt");
              //添加对象过滤流
              ObjectOutputStream oos=new ObjectOutputStream(fos);
              ObjectInputStream ois=new ObjectInputStream(fis)
                ){
            //先写
            oos.writeDouble(10.5);
            //强刷缓冲区
            oos.flush();
            //再读
            System.out.println(ois.readDouble());
        }catch...
    }
}

读写引用类型-String

类库中的引用类型

Object ois.readObject(): 读取引用类型
void oos.writeObject(Object o): 写入引用类型

使用

  1. readObject()|readXxx()读取到达末尾会抛出EOFException
  2. writeObject()自带缓冲区刷新, 先写后读时无需手动刷新缓冲区
package com.by.test;

import java.io.*;

public class Test_object_String {
    public static void main(String[] args) {
        //先往file/a.txt中写入一些String, 再读取输出
        try(
              FileOutputStream fos=new FileOutputStream("file/a.txt");
              FileInputStream fis=new FileInputStream("file/a.txt");
              //添加对象过滤流
              ObjectOutputStream oos=new ObjectOutputStream(fos);
              ObjectInputStream ois=new ObjectInputStream(fis)
                ){
            //先写
            oos.writeObject("一二三四五");
            oos.writeObject("上山打老虎");
            oos.writeObject("老虎没打着");
            oos.writeObject("打着小松鼠");

            System.out.println("写入成功!");
            //读取输出
            while (true) {
                try {
                    String str = (String) ois.readObject();
                    System.out.println(str);
                } catch (EOFException e) {
                    //读取到达末尾
                    break;
                }
            }

        }catch...
    }
}
读写自定义类型

自定义类型必须实现Serializable接口, 意味着允许被序列化(IO流可以读取类的完整信息)

可以通过给属性添加transient修饰符的方式防止某些属性参与序列化

public class Student implements Serializable {
    private String name;
    //防止被序列化
    private transient int age;
    private double score;
    
    ....
}

序列化: 将对象相关的所有内容进行读取的过程

反序列化: 读取相关信息组装成对象的过程

//先往file/a.txt中写入一个学生对象, 再读取输出
        try(
              FileOutputStream fos=new FileOutputStream("file/a.txt");
              FileInputStream fis=new FileInputStream("file/a.txt");
              //添加对象过滤流
              ObjectOutputStream oos=new ObjectOutputStream(fos);
              ObjectInputStream ois=new ObjectInputStream(fis)
                ){
            //先写- 序列化对象信息
            oos.writeObject(new Student("zhangsan", 20, 90));
            //读取输出- 反序列化对象
            Student stu =(Student) ois.readObject();
            System.out.println(stu);
        }catch...
  • 23
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值