java基础(十二):IO流

IO流

  • 流动的是数据

    • I:Input — 输入
    • O: Output — 输出
  • 分类

    1. 方向:以当前程序为基准

      • 输入流:从外部流入到程序中
      • 输出流:从程序内存流入到外部
    2. 数据单元: 字节流 & 字符流

    四种流:

    • 字节输入流 :InputStream & 字节输出流 :OutputStream

    • 字符输入流 :Reader & 字符输出流 :Writer

    • 以上四种基类都是抽象类

  • 数据来源:硬盘、内存、网络、外部的设备

文件相关流

  • File +字符流组合:文本文件以字符形式读取

    FileReader — 文件字符输入流 & FileWriter — 文件字符输出流

  • File + 字节流组合:图片、文本文档、音频、视频以字节形式读取

    FileInputStream — 文件字节输入流 & FileOutputStream — 文件字节输出流

FileReader

  • 构造方法:

    • FileReader(File file):读取指定的File文件。

    • FileReader(String fileName) :通过给定文件路径名用来创建文件读取流

  • 方法:

    • int read():读取单个字符 ,如果已到达流的末尾,则返回 -1
    • int read(char[] cbuf):将字符读入数组 ,如果已到达流的末尾,则返回 -1;返回值代表读取的字符个数

注意

  • 默认使用read()读取数据时,是一个字符一个字符的读取,效率较低。为提高效率,手动创建一个字符数组(缓冲数组)提高效率

  • 关于数组缓冲,为了提高效率以及内存利用率,每次读取不会清空数组,所以当我们拿数据时需要指定开始下标以及元素个数

    public class FileReaderDemo1 {
    public static void main(String[] args) throws IOException {
    // 创建FileReader类
    // 如果给定路径的文件不存在,则抛出java.io.FileNotFoundException异常
    // FileReader reader = new FileReader("D:\\Javase\\Day18\\src\\aa\\aa.txt");
    File file = new File("D:\\Javase\\Day19\\src\\aa\\a.txt");
    FileReader reader = new FileReader(file);
    System.out.println(reader);  // 没有重写,打印地址值
    // read()
    //        int i;
    //        while ((i = reader.read()) != -1){
    //            System.out.println((char)i);
    //        }
    // 手动指定一个缓冲数组,tigaoxiaolv
    char[] chars = new char[6];
    // int num = reader.read(chars);
    // System.out.println(num+"---"+ Arrays.toString(chars));
    int num;
    while((num = reader.read(chars))!=-1){
    String str = new String(chars,0,num);  // 指定开始下标以及元素个数
    System.out.println(str);
    }
    }
    }
    

FileWriter

  • 构造方法:

    • FileWriter(File file):根据给定的 File 对象构造一个 FileWriter 对象。

    • FileWriter(String fileName):根据给定的文件名构造一个 FileWriter 对象。

    • FileWriter(File file, boolean append): 如果第二个参数为 true,则追加写入内容。如果为false,则效果和没有第二个参数是一样的

    • FileWriter(String fileName, boolean append) 如果第二个参数为 true,则追加写入内容。如果为false,则效果和没有第二个参数是一样的

  • 方法:

    • void write(int c) :写入单个字符。

    • flush() : 刷新该流的缓冲;将缓冲中的数据刷到文件

    • void write(String str):写入字符串

    • void write(char[] cbuf):写入字符数组。

    • close():关闭此流,但要先刷新它。

注意:

  1. 如果文件不存在,则创建新的文件;默认情况下如果文件已经存在,则生成新的空文件把原文件覆盖了;append表示追加写
  2. FileWriter类底层自定义了一个缓冲数组,当写出数据时,先将数据写入到缓冲中
  3. 因为流会占用较大的资源,所以每次使用完流必须通过close()方法关闭;如果后面不再需要流对象,则可置为null
  4. 关闭流之前,底层会自动将缓存中的数据刷新到文件中

FileInputStream

  • 构造方法:

    • FileInputStream(File file):根据File来构建流对象

    • FileInputStream(String name):根据字符串路径名构造流对象

  • 方法

    • int read():从此输入流中读取一个数据字节 ,已到达文件末尾,则返回 -1
    • int read(byte[] b): 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中;返回读入缓冲区的字节总数,如果读完了,返回-1
    public class FileInputStreamDemo {
    public static void main(String[] args) throws IOException {
    FileInputStream in = new FileInputStream("D:\\Javase\\Day19\\src\\aa\\file.txt");
    // read() --- 每次读取一个字节
    // System.out.println((char)in.read());
    // read(byte[] b)
    byte[] bytes = new byte[6];
    // int num = in.read(bytes);
    // System.out.println(new String(bytes));
    // 遍历
    int num;
    StringBuilder builder = new StringBuilder();
    while((num = in.read(bytes))!=-1){
    builder.append(new String(bytes,0,num));
    }
    System.out.println(builder);
    in.close();
    }
    }
    

FileOutputStream

  • 构造方法:

    • FileOutputStream(File file):创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
    • FileOutputStream(File file, boolean append) : 创建一个向指定 File 对象表示的文件中写入数据的文件输出流。
    • FileOutputStream(String name):创建一个向具有指定名称的文件中写入数据的输出文件流。
    • FileOutputStream(String name, boolean append):创建一个向具有指定 name 的文件中写入数据的输出文件流。
    • 方法:
    • void write(int b):将指定字节写入此文件输出流。
    • void write(byte[] b) :将 b.length 个字节从指定 byte 数组写入此文件输出流中。

    示例:复制图像

    public class FileOutputStreamDemo {
        public static void main(String[] args) throws IOException {
            // 复制图片
            File file = new File("D:\\Javase\\Day19\\src\\aa\\fig.png");
            // 读取
            FileInputStream in = new FileInputStream(file);
            // 输出
            FileOutputStream out = new FileOutputStream("D:\\Javase\\Day19\\src\\aa\\bb\\"+file.getName());
    
            byte[] bytes = new byte[6];
            int num;
            while ((num = in.read(bytes)) != -1){
                out.write(bytes,0,num);
            }
            in.close();
            out.close();
    
        }
    }
    

流的关闭异常处理

  1. 外置定义流对象,初始值给为null

  2. try catch包裹异常代码

  3. finally中关闭流,但是调用close方法之前,需要确定流对象不为null;否则会抛出空指针异常

  4. 因为close也有异常,也需要try catch捕获

  5. finally中为了释放对象资源,将流对象设置为null

示例:

public class FileWriterDemo2 {
    public static void main(String[] args) {
        // 创建FileWriter对象
        // 如果文件不存在,则创建新的文件
        FileWriter writer = null;
        try {
            writer = new FileWriter("D:\\Javase\\Day19\\src\\aa\\file.txt");
            // write()
            writer.write('a');  // char -- int 自动类型转换
            writer.write("\nhello java!\n");
            char[] chars = new char[]{'e','n','d','!'};
            writer.write(chars);
            writer.flush();  // 刷新数据
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (writer != null) {
                try {
                    writer.close();
                } catch (IOException e) {
                    e.printStackTrace();
                } finally {
                    writer = null;
                }
            }
        }
    }
}

IO 缓冲流

  • 字节流:BufferedInputStream & BufferedOutputStream

  • 字符流:BufferedReader & BufferedWriter

缓冲字节输入流: BufferedInputStream

  • 构造方法:

    • BufferedInputStream(InputStream in) :将参数字节输入流in存储下来,在in的基础上增加了缓冲
    • BufferedInputStream(InputStream in, int size) :创建具有指定缓冲区大小的 BufferedInputStream 并保存其参数,即输入流 in,以便将来使用
  • 方法:

    • int read():从此输入流中读取一个数据字节 ,已到达文件末尾,则返回 -1
    • int read(byte[] b): 从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中;返回读入缓冲区的字节总数,如果读完了,返回-1
    public class BufferedInputStreamDemo {
        public static void main(String[] args) throws IOException {
            BufferedInputStream bin = new BufferedInputStream(
                    new FileInputStream("D:\\Javase\\Day19\\src\\aa\\file.txt"));
            // read()
            int i;
            while((i = bin.read()) != -1){
                System.out.println((char) i);
            }
        }
    }
    

缓冲字节输出流: BufferedOutputStream

  • 构造方法:

    • BufferedOutputStream(OutputStream out):创建一个新的缓冲输出流,以将数据写入指定的底层输出流。
    • 方法:同 FileOutputStream
    public class BufferedOutputStreamDemo {
        public static void main(String[] args) throws IOException {
            BufferedOutputStream bout = new BufferedOutputStream(
                    new FileOutputStream("D:\\Javase\\Day19\\src\\aa\\file2.txt"));
            // write()
            bout.write("alksdjfk".getBytes());
            // 因为底层设置了缓冲区,所以需要flush()将缓冲区的数据刷新到文件中
            bout.flush();
            // 关流
            bout.close();
        }
    }
    

注意

  1. 缓冲流实际上是在字节流的基础上包装了关于缓冲区的操作;但是这些操作都被隐藏了,对于用户来说,使用缓冲流其实和字节流基本是一致的;

  2. 缓冲流效率上要比字节流要高

缓冲字符输入流:BufferedReader

  • 构造方法:

    • BufferedReader (Reader in) :创建一个使用默认大小输入缓冲区的缓冲字符输入流。
  • 方法 — 在FileReader上新增方法

    • String readLine():读取一个文本行。如果已到达流末尾,则返回 null
    public class BufferedReaderDemo {
        public static void main(String[] args) throws IOException {
            // BufferedReader
            BufferedReader reader = new BufferedReader(
                    new FileReader("D:\\Javase\\Day19\\src\\aa\\file.txt"));
            // 读取数据
            // String line = reader.readLine();
            // System.out.println(line);
            String line = null;
            // 遍历读取所有内容
            // readLine方法当读取完成则返回null
            while((line = reader.readLine()) != null){
                System.out.println(line);
            }
        }
    }
    

缓冲字符输出流:BufferedWriter

  • 构造方法

    • BufferedWriter (Writer in) :根据Writer实现类创建一个带有缓冲区的缓冲流
  • 方法 — 在FileWriter上新增方法:

    • void newLine() :写入一个行分隔符。
    public class BufferedWriterDemo {
        public static void main(String[] args) throws IOException {
            // 创建缓冲输出流对象
            BufferedWriter writer = new BufferedWriter(
                    new FileWriter("D:\\Javase\\Day19\\src\\aa\\file1.txt")
            );
            // 写出数据到文件
            writer.write("hello java");
            writer.newLine();
            writer.write("end");
            // 刷新
            writer.flush();
            // 关闭流
            writer.close();
        }
    }
    

总结:如果在使用流读取或者写入数据时,为提高效率,建议使用缓冲流

转换流

  • 字符流操作文件,如果是中文可能出现中文乱码问题

  • 记事本编辑的文件编码是GBK(中文占用两个字节)

  • idea用的编码是UTF-8(中文占用三个字节)

  • 概述:字节和字符之间的相互转换

    • 字节 — > 字符:InputStreamReader
    • 字符 — > 字节:OutputStreamWriter

InputStreamReader

  • 构造方法:
    • InputStreamReader (InputStream in, String charsetName) : 创建使用指定字符集的 InputStreamReader。

OutputStreamWriter

  • 构造方法:
    • OutputStreamWriter (OutputStream out, String charsetName):创建使用指定字符集的 OutputStreamWriter。
public class FileCopy {
    public static void main(String[] args) throws IOException {
        // 文件复制
        // 定义输入流
        FileInputStream in = new FileInputStream("D:\\Javase\\Day19\\src\\aa\\file.txt");
        InputStreamReader reader = new InputStreamReader(in,"GBK");
        // 定义输出流
        FileOutputStream out = new FileOutputStream("D:\\Javase\\Day19\\src\\aa\\file1.txt");
        OutputStreamWriter writer = new OutputStreamWriter(out,"GBK");
        // 指定一个缓冲数组
        char[] chars = new char[6];
        int num;
        while((num = reader.read(chars)) != -1){
            writer.write(chars,0,num);
        }
        // in.close()
        reader.close();
        // out.close()
        // 因为out是writer包裹的一个类,当调用writer的close()时
        // 底层会默认调用out的close()
        writer.close();
    }
}

其他流

  1. 内存

    • StringReader:用来读取字符串的流

      构造方法:

      • StringReader(String s):创建一个新字符串 reader。
    • StringWriter:写出字符串

      构造方法:

      • StringWriter(): 使用默认初始字符串缓冲区大小创建一个新字符串 writer。

      • StringWriter(int initialSize):使用指定初始字符串缓冲区大小创建一个新字符串 writer。

        public class StringReaderDemo {
            public static void main(String[] args) throws IOException {
                String str = "hello java";
                StringReader reader = new StringReader(str);
        
                int ch;
                while((ch = reader.read()) != -1){
                    System.out.println((char) ch);
                }
            }
        }
        
  2. 外部设备

    • System.in : 标准输入流

    • System.out: 标准输出流

    • System.err : 标准错误流 红色字体

       public class PrintContent {
           public static void main(String[] args) throws IOException {
               // 从键盘获取信息存入文档
               BufferedInputStream bin = new BufferedInputStream(System.in);
               BufferedOutputStream bout = new BufferedOutputStream(
                       new FileOutputStream("D:\\Javase\\Day19\\src\\aa\\fileTest.txt"));
               int num;
               while((num = bin.read()) != '#'){
                   bout.write((char) num);
               }
               bin.close();
               bout.close();
           }
       }
      
  3. 打印流

    • PrintStream:打印流:提供了多种print方法,用于对各种数据类型进行输出打印

    • PrintWriter:适用处理字符相关的内容

序列化和反序列化

  • 序列化 — ObjectOutputStream :将对象当前的状态存储下来
  • 反序列化 — ObjectInputStream :将存储对象状态恢复回来
  • 常用于网络传输

ObjectOutputStream

  • 将 Java 对象的基本数据类型和图形写入 OutputStream

  • 前提:

  • 要序列化的对象,必须实现一个接口Serializable;该接口中没有任何的方法,它的作用时用来做标识;如果没有实现该接口,抛出异常 — NotSerializableException

  • 构造方法:

    • ObjectOutputStream(OutputStream out) :传入参数用来指定序列化之后的内容传到什么位置
  • 方法:

    • void writeObject(Object obj) :将指定的对象写入 ObjectOutputStream。

ObjectInputStream

  • 构造方法

    • ObjectInputStream(InputStream in) :创建从指定 InputStream 读取的 ObjectInputStream。
  • 方法:

    • Object readObject(): 从 ObjectInputStream 读取对象。
package cn.zss.io.xuliehua;
import java.io.Serializable;

class Person implements Serializable {
    private static final long serialVersionUID = 1L;  // 如果没有手动给定,程序会自动生成
    private String name;
    private int age;
    private transient char gender;  // 不会被序列化
    private  static final int eyeNum = 2;  // 不会被序列化

    public Person(String name, int age){
        this.name = name;
        this.age = age;
    }
    
    public static int getEyeNum() {
        return eyeNum;
    }

    public void setGender(char gender) {
        this.gender = gender;
    }
    
    public char getGender() {
        return gender;
    }
    
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}
package cn.zss.io.xuliehua;
import java.io.*;

public class PersonDemo  {
    public static void main(String[] args)  throws IOException {
        Person person = new Person("Sam",20);
        person.setGender('男');
        System.out.println(Person.getEyeNum());

        // 将当前person对象进行序列化
        // 指定序列化之后将内容存储到文件中
        FileOutputStream fos = new FileOutputStream("D:\\Javase\\Day19\\src\\aa\\person.txt");
        // 创建序列化对象
        ObjectOutputStream out = new ObjectOutputStream(fos);
        // 调用方法序列化指定对象
        out.writeObject(person);
        // 关闭流
        out.close();
    }
}
package cn.zss.io.xuliehua;
import java.io.*;

public class PersonDemo2 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        // 反序列化
        // 指定反序列化的文件
        File file = new File("D:\\Javase\\Day19\\src\\aa\\person.txt");
        // 输入流读取文件内容
        FileInputStream in = new FileInputStream(file);
        // 创建反序列化流
        ObjectInputStream ois = new ObjectInputStream(in);

        // 创建反序列化方法,来创建对象
        Object o = ois.readObject();

        // 强制转换为Person类型
        Person p =  (Person) o;
        System.out.println(p);
        System.out.println(Person.getEyeNum());
        System.out.println(p.getGender());  // null

        // 关流
        ois.close();
    }
}

注意:

  1. 静态变量不会被序列化

  2. transient修饰的变量也不会被序列化;如果该对象反序列化之后,对于transient修饰的变量值会给数据类型默认值

  3. serialVersionUID 版本号

    • 当实现Serializable接口,默认会给当前对象新增一个成员:private static final long serialVersionUID;

    • 如果没有手动给定,程序会根据当前类的属性和方法自动生成;当类的属性或者方法发生变化时,该值相应也会发生变化;

    • 反序列化一个对象时,会先和类中的版本号进行对比,如果版本号一致,可以序列化;如果对比不一致,则会抛出异常

  4. 序列化与反序列:第三方的插件 avro

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值