Java笔记一 IO流

210 阅读9分钟

字节流

IO流概述和分类

IO流分类
1. 按照数据的流向
    输入流: 读数据
    输出流: 写数据
2. 按照数据类型来分
    字节流
        字节输入流, 字节输出流
    字符流
        字符输入流, 字符输出流
        
一般来说,我们IO流的分类是按照数据类型来分的
那么这俩种流在什么情况下使用?
如果数据我们能够读的懂使用字符流,如果读不懂使用字节流
字节流写数据
字节流抽象基类
InputStream: 这个抽象类是表示字节输入流的所有类的超类
OutputStream: 这个抽象类是表示字节输出流的所有类的超类
子类名特点:子类名称都是以其父类名作为子类名的后缀

FileOutputStream: 文件输出流用于将数据写入File
FileOutputStream(String name): 创建文件输出流以指定的名称写入文件

使用字节输出流写数据的步骤:
    1.创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流对象指向文件)
    2.调用字节输出流对象的写数据方法
    3.释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
字节流写数据的三种方式
方式1: 一个个字节写入
    fileOutputStream.write(97);
    fileOutputStream.write(98);
    fileOutputStream.write(99);
    fileOutputStream.write(100);
    fileOutputStream.write(101);
方式2: 以字节数组为单位写入数据
    byte[] bytes = {97, 98, 99, 100, 101};
    fileOutputStream.write(bytes);
    
方式3: 以字符串转为字节数组
    fileOutputStream.write("abcde".getBytes());
    fileOutputStream.write("abcde".getBytes(), 1, 3);

字节流读数据(一次读一个字节数据)

FileInputStream: 从文件系统中的文件获取输入字节
FileInputStream(String name): 通过打开与实际文件的连接来创建一个FileInputStream, 该文件由文件系统中的路径名name命名

使用字节输入流读数据的步骤:
1.创建字节输入流对象
2.调用字节输入流对象的读数据方法
3.释放资源

public static void fileInputStream() {
    FileInputStream fs = null;
    try {
        fs = new FileInputStream("day10File/fos.txt");
        int read;
        while ((read = fs.read()) != -1) {
            System.out.print((char) read);
        }
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (fs != null) {
            try {
                fs.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

字节缓冲流

BufferedOutputStream: 
该类实现缓冲输出流。通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
BufferedInputStream:
创建BufferedInputStream将创建一个内部缓冲区数组,当从流中读取或跳过字节时,内部缓冲区将
根据需要从所包含的输入流中重新填充,一次很多字节
构造方法:
    字节缓冲输出流: BufferedOutputStream(OutputStream out)
    字节缓冲输入流: BufferedInputStream(InputStream in)
为什么构造方法需要的是字节流,而不是具体的文件或者路径?
字节缓冲流仅仅提供缓冲区,而真正的读写数据还得依靠基本的字节流对象进行操作

public static void bufferedOS() throws IOException {
    BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("day10File/bos.txt"));
    bos.write("Hello\n".getBytes());
    bos.write("world\n".getBytes());
    bos.close();

}

public static void bufferedIS() throws IOException {

    BufferedInputStream bis = new BufferedInputStream(new FileInputStream("day10File/bos.txt"));
    byte[] bytes = new byte[1024];
    int len;
    while ((len = bis.read(bytes)) != -1) {
        System.out.println(new String(bytes, 0, len));
    }
    bis.close();
}

字符流

1.为什么会出现字符流
    由于字节流操作中文不是特别的方便,所以Java就提供字符流
    字符流 = 字节流 + 编码表
    用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动
    进行字节拼接成中文,如何识别中文的呢?
    汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数

字符串中的编码解码问题

编码
    byte[] getBytes():
    使用平台的默认字符集将该String编码为一系列字节,将结果存储到新的字节数组中
    byte[] getBytes(String charsetName): 
    使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
解码
    String(byte[] bytes): 
    通过使用平台的默认字符集解码指定的字节数组来构造新的String
    String(byte[] bytes, String charsetName): 
    通过指定的字符集解码指定的字节数组来构造新的String
    
    
public static void characterSet() throws UnsupportedEncodingException {
    String s = "中国";
    // 使用平台默认字符集
    byte[] bytes = s.getBytes();    // [-28, -72, -83, -27, -101, -67]
    System.out.println(new String(bytes));
    System.out.println(Arrays.toString(bytes));
    // 使用GBK编码
    byte[] gbks = s.getBytes("GBK");    // [-42, -48, -71, -6]
    //  System.out.println(new String(gbks));   // 乱码
    System.out.println(new String(gbks, "gbk"));
    System.out.println(Arrays.toString(gbks));
}

字符流中的编码解码问题

InputStreamReader: 是从字节流到字符流的桥梁
    它读取字节,并使用指定的编码将其解码为字符
OutputStreamWrite: 是从字符流到字节流的桥梁
    使用指定的编码将写入的字符编码为字节
    
public static void characterStream() throws IOException {
    // 默认平台字符集
    OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("day10File/character.txt"));
    osw.write("中国");
    osw.close();

    // 指定字符集
    OutputStreamWriter osw2 = new OutputStreamWriter(new FileOutputStream("day10File/character2.txt"), "gbk");
    osw2.write("中国");
    osw2.close();

    // 指定字符集读取内容
    InputStreamReader isr = new InputStreamReader(new FileInputStream("day10File/Character2.txt"), "gbk");
    int bys;
    while ((bys = isr.read()) != -1) {
        System.out.print((char)bys);
    }
    isr.close();
}
字符流写数据的5种方式
构造方法:
    OutputStreamWriter(OutputStream out): 创建一个使用默认字符集的OutputStreamWrite
void write(int c): 写一个字符
void write(char[] cbuf): 写入一个字符数组
void write(char[] cbuf, int off, int len): 写入字符数组的一部分
void write(String str): 写一个字符串
void write(String str, int off, int len): 写一个字符串的一部分
flush() 刷新流,还可以继续写数据
close() 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能写数据
字符流读数据的2中方式
构造方法:
    InputStreamReader(InputStream in): 创建一个使用默认字符集的InputStreamReader
int read()  一次读一个字符数据
int read(char[] cbuf) 一次读一个字符数组数据

FileReader, FileWrite

转换流的名字比较长,而我们的常见操作都是按照本地默认编码实现的(如果指定字符集使用上述转换流),所以,为了简化书写,转换流提供了对应的子类
FileReader: 用于读取字符文件的便捷类
    FileReader(String fileName)
FileWrite: 用于写入字符文件的便捷类
    FileWrite(String fileName)
数据源和目的地的分析
    数据源: 读数据---Reader---InputStreamReader---FileReader
    目的地: 写数据---Write---OutputStreamWrite---FileWrite
    
public static void copyFile2() throws IOException {
    String origin = "day10File/src/cn/lonsun/IO/CharacterDemo.java";
    FileReader fileReader = new FileReader(origin);
    FileWriter fileWriter = new FileWriter("day10File/BufferedDemo2.java");
    // 读
    char[] chars = new char[1024];
    int len;
    while ((len = fileReader.read(chars)) != -1) {
        fileWriter.write(chars, 0, len);
    }

    fileReader.close();
    fileWriter.close();
}

字符缓冲流

BufferedWrite: 将文本写入字符流,缓冲字符,已提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途
BufferedRead: 从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。默认值足够大,可用于大多数用途

public static void buffered() throws IOException {
    System.out.println("------------");
    BufferedWriter bw = new BufferedWriter(new FileWriter("day10File/CharBuffered.txt", true));
    bw.write(97);
    char[] chars = {'a', 'b'};
    bw.write(chars);
    bw.write(chars, 0, 1);
    bw.write("\nHello");
    bw.write("\nWorld", 0, "\nWorld".length());
    bw.close();

    // 字符缓冲流读取
    BufferedReader br = new BufferedReader(new FileReader("day10File/CharBuffered.txt"));
    char[] chars1 = new char[1024];
    int len;
    while ((len = br.read(chars1)) != -1) {
        System.out.println(new String(chars1, 0, len));
    }
    br.close();
}

字符缓冲流特有的功能
    BufferedWrite:
        void newLine(): 写一行行分隔符,航分隔符字符串由系统属性定义
    BufferedRead:
        public String readLine(): 读一行文字。结果包含行的内容的字符串,不包含任何终止字符,如果流的结尾已经到达,则为null
        
案例        
public static void bufferedMethod() throws IOException{
    System.out.println("------特有的方法-------");
    BufferedWriter bw = new BufferedWriter(new FileWriter("day10File/method.txt"));
    BufferedReader br = new BufferedReader(new FileReader("day10File/method.txt"));
    for (int i = 0; i < 5; i++) {
        bw.write("hello" + i);
        bw.newLine();
        bw.flush();
    }
    bw.close();

    char[] chars = new char[1024];
    String value;
    while ((value = br.readLine()) != null) {
        System.out.println(value);
    }

    br.close();
}

IO流小结

特殊操作流

标准输入输出流
System类中有俩个静态的成员变量
1.public static final InputStream in:
标准输入流。通常该流对应于键盘输入或主机环境或用户指定的另一个输入源

 自己实现键盘录入数据
BufferedReader br = new BufferedReader(new InputStreamReader(System.in))
写起来太麻烦,Java就提供了一个类实现键盘录入
Scanner sc = new Scanner(System.in)

2.public static final OutputStream out:
标准输出流。通常该流对应于显示输出或有主机环境或用户指定的另一个输出目标

输出语句的本质: 一个标准的字节输出流
PrintStream ps = System.out
PrintStream类有的方法,System.out都可以使用
打印流
打印流分类
    1.字节打印流: PrintStream
        PrintStream(String fileName): 使用指定的文件名创建新的打印流
        使用继承父类的方法写数据,查看的时候会转码;使用自己的特有方法写数据,查看的数据原样输出
    2.字符打印流: PrintWrite
        PrintWrite(String fileName): 使用指定的文件名创建一个新的PrintWrite
        PrintWrite(Write out, boolean autoFlush):
        创建一个新的PrintWrite,out: 字符输出流,autoFlush: 一个布尔值,如果为真,则println,print,format方法将刷新输出缓冲区
打印流的特点:
    只负责输出数据,不负责读取数据
    有自己的特有方法

对象序列化流

对象序列化流: ObjectOutputStream
将Java对象的原始数据类型和图形写入OutputStream。可以使用ObjectInputStream重构对象。可以通过使用流的文件来实现对象的持久存储。如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象

构造方法:
    ObjectOutputStream(OutputStream out): 创建一个写入指定的OutputStream的ObjectOutputStream
序列化对象的方法:
    void writeObject(Object obj): 将指定的对象写入ObjectOutputStream
    注意:
        一个对象要想被序列化,该对象所属的类必须实现Serializable接口
        Serializable是一个标记接口,实现该接口,不需要重写任何方法
    
        
对象反序列化流: ObjectInputStream
构造方法: 
    ObjectInputStream(InputStream in): 创建从指定的InputStream读取的ObjectInputStream
    反序列化对象的方法:
    Object readObject(): 从ObjectInputStream读取一个对象
    
private static void storeObject() throws IOException, ClassNotFoundException {
    String origin = "day10File/objectStream.txt";
    Student student = new Student("高露", 28);
    ObjectOutputStream outputStream = new ObjectOutputStream(new FileOutputStream(origin));
    outputStream.writeObject(student);
    outputStream.close();

    // 读取流生成对象
    ObjectInputStream ois = new ObjectInputStream(new FileInputStream(origin));
    Student s = (Student) ois.readObject();
    System.out.println(s);
}