【JavaSE】之IO流

发布于:2022-11-29 ⋅ 阅读:(176) ⋅ 点赞:(0)


前言

本文为Java基础IO流相关知识,Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~

一、字节输出流(OutputStream)

1.字节输出流简介

  • 字节输出流:OutputStream(byte)
  • 抽象类,是所以输出字节流的超类

包含的成员方法:

public void close():关闭输出流并释放与此流相关联的任何系统资源。
public void flush():刷新此输出流并强制任何缓冲的输出字节被写出。
public void write(byte[] b):将b.length的字节从指定的字节数组写入此输出流
public void write(byte[] b,int off,int len):从指定的字节数组写入 len 字节,从偏移量 off 开始输出到此输出流
public abstract void write(int b):将指定的字节写入此输出流。 (一个字节)

2.子类:FileOutputStream

构造方法:

FileOutputStream(File file) 
创建文件输出流以写入由指定的 File对象表示的文件。 
FileOutputStream(String name) 
创建文件输出流以指定的名称写入文件。  
参数:写入数据的目的地。
    file:目的地是一个文件
    string:目的地是一个文件的路径
构造方法的作用:
    1.创建FileOutputStream对象。
    2.会根据构造方法中传递的文件/文件路径,创建一个文件(如果存在则不会创建)
    3.会把FileOutputStream对象指向创建好的文件(构造方法不是写入的方法,而是准备工作)

流的使用步骤

  • 1.创建FileOutputStream对象,构造方法中写,写入数据的目的地。
  • 2.调用FileOutputStream对象中的write方法,把数据写入到文件中
  • 3.释放资源
  • 4.写入数据原理:java程序通知java虚拟机JVm,通过虚拟机通知操作系统,操作系统调用写入数据的方法,把数据写入

注意:使用时程序会抛出异常,需要对异常进行处理
代码实现:

import java.io.FileOutputStream;
import java.io.IOException;
public class Java_io_01 {
    //将异常从方法中抛出
    public static void main(String[] args) throws IOException {
        FileOutputStream fos=new FileOutputStream("C:\\Users\\邹飞鸣\\Desktop\\学习资料\\2020.7.14(暑假)\\markdown学习笔记\\javaIO流笔记\\练习Out流.txt");
        fos.write('s');      
        fos.close();
    }
}
// 结果:在相应的文件夹下面产生了一个对应的文件,并写入了's'

注意:1、存储的数会以二进制形式存储下来,如果存储97这个数值时,真正存储的是字符’a’,即二进制会转换成ASCII表中对应的数值,如果数值超过127,则会去查找系统默认的码(GBK等等)并表示。2、如果是(-97,-98,-97,98)这种情况,98一个是正数,一个是负数,即使是这样-97-98和-9798所表示的值还是不一样。

一次输入多个字节

 -  public void write(byte[] b):将b.length的字节从指定的字节数组写入此输出流
 -  public void write(byte[] b,int off,int len):从指定的字节数组写入 len 字节,从偏移量 off 开始输出到此输出流
    两个方法,如果第一个字节是负数(如:-1,2),那么第一个字节会和第二个字节合并作为一个中文显示,查询的是系统默认码表

代码实现:

// 1.  public void write(byte[] b):将b.length的字节从指定的字节数组写入此输出流
import java.io.FileOutputStream;
import java.io.IOException;
public class Java_io_01 {
    public static void main(String[] args) throws IOException {
        byte[] bytes={-97,-98,-97,98,101,102};
        FileOutputStream fos=new FileOutputStream("javaIO流Out类测试文件.txt");
        fos.write(bytes);
        fos.close();
    }
}
-----------------------------------------------------------------------------------------------------------
// 2.public void write(byte[] b,int off,int len):从指定的字节数组写入 len 字节,从偏移量 off 开始输出到此输出流 
// 这个方法可以指定输入的字节
// off:在数组中,开始写入字节的位置
// len:写入字节的位数
// 代码实现:
import java.io.FileOutputStream;
import java.io.IOException;
public class Java_io_01 {
    public static void main(String[] args) throws IOException {
        byte[] bytes={-97,-98,-97,98,101,102};
        FileOutputStream fos=new FileOutputStream("javaIO流Out类测试文件.txt");
        //定义字节录入位置和长度
        fos.write(bytes,0,2);
        fos.close();
    }
}

可以写入字符串

  • 字符串类中有一个将字符串转换成字节数组的方法:byte[] getBytes();
  • 转换后就可以作为字节利用write方法录入文件了。

换行写以及续写
续写:

// 每次重新运行程序,如果有相同文件名,是直接覆盖原来的文件及其内容。那如果想不覆盖而是在其后继续录入字符串就需要用到这个类的另外两个构造方法:
//       FileOutputStream(String name, boolean append) 创建文件输出流以指定的名称写入文件。
//       FileOutputStream(File file, boolean append) 创建文件输出流以写入由指定的 File对象表示的文件。 
//       参数:append作用是一个开关,为true时创建对象不会覆盖源文件,而是继续在文件的末尾追加数据,为false时,创建新文件,覆盖源文件
// 代码:
import java.io.FileOutputStream;
import java.io.IOException;
public class Java_io_01 {
    public static void main(String[] args) throws IOException {
        byte[] bytes={97,98,97,98,101,102};
        //正常录入
        FileOutputStream fos=new FileOutputStream("javaIO流Out类测试文件.txt");
        fos.write(bytes,0,2);
        fos.close();
        //打开续写开关后的录入
        FileOutputStream fos1=new FileOutputStream("javaIO流Out类测试文件.txt",true);
        fos1.write(bytes,0,2);
        fos1.close();
    }
}
// 结果: abab

换行:

  • 写换行符,windows的是:\r\n ,Linux是:/n
  • 注意:换行符只能作为字符串写入。
import java.io.FileOutputStream;
import java.io.IOException;
public class Java_io_01 {
    public static void main(String[] args) throws IOException {
        byte[] bytes={97,98,97,98,101,102};
        FileOutputStream fos=new FileOutputStream("javaIO流Out类测试文件.txt");
        for (byte i:bytes){
            fos.write(i);
            //用字符串来调用方法,将字符串转换成字节数组
            fos.write("\r\n".getBytes());
        }
        fos.close();
    }
}
// 结果:
 //    a
 //    b
 //    a
 //    b
 //    e
 //    f

二、字节输入流(OutputStream)

1.字节输入流简介

共性方法:

  • int read() 从该输入流读取一个字节的数据。 读取完成后最后会返回一个-1
  • int read(byte[] b) 从该输入流读取最多 b.length个字节的数据为字节数组。
  • void close() 关闭此文件输入流并释放与流相关联的任何系统资源。
  • 还有其他的…

2.子类:FileInputStream

简介:

  • 文件字节输入流
  • 作用:把硬盘文件中的数据,读取到内存中

构造方法:

  • FileInputStream(File file) 通过打开与实际文件的连接创建一个 FileInputStream,该文件由文件系统中的 File对象 file命名。
  • FileInputStream(String name) 通过打开与实际文件的连接来创建一个 FileInputStream,该文件由文件系统中的路径名 name命名。
  • 作用:会创建一个FileInputStream对象并指向构造方法中要读取的文件

读取文件内容:
创建FileInputStream对象,调用read方法。
代码实现:

import java.io.FileInputStream;
import java.io.IOException;
public class Java_Io_02 {
    public static void main(String[] args) throws IOException {
        int i=0;
        FileInputStream fis=new FileInputStream("javaIO流Out类测试文件.txt");
        //判断文件是否为空,若不空,则读取。
       while ((i=fis.read())!=-1){
            System.out.print(i+",");
        }
       fis.close();
    }
}
// 结果:
//     97,13,10,98,13,10,97,13,10,98,13,10,101,13,10,102,13,10,
// 原因:文件中本来只存储了,
// a
// b
// a
// b
// e
// f
// 但是除了会自动转换成int类型外,多出来的13,10是换行符,起换行作用,且文件中没有显示,但是存在

注意:记得释放资源

一次读取多个字节:

  • 将字节数组全部内容转换为字符串:String(byte[] bytes) 通过使用平台的默认字符集解码指定的字节数组来构造新的String 。(是String类的构造方法,还有转换部分字节的构造方法,这里撇下不表)
  • 创建FileInputStream对象,创建字节数组接收,字节数组的大小就是一次接收的多少,int read(byte[] b)方法的返回值是:读取的有效字节数。

代码实现:

import java.io.FileInputStream;
import java.io.IOException;
public class Java_Io_02 {
    public static void main(String[] args) throws IOException {
        //定义i为判断返回值是否为-1,即是否取完
        int i=0;
        FileInputStream fis = new FileInputStream("javaIO流Out类测试文件.txt");
        //定义字节数组,大小是2的倍数最好
        byte[] bytes = new byte[1024];
        //调用read方法取字节存储到字节数组中,并将有效字节数返回并赋值给i
       while((i=fis.read(bytes))!=-1){
           //将字节数组中的值取出来用String的构造函数转换成字符串数组,取值范围是0到i(i为有效值)
           System.out.print(new String(bytes,0,i));
       }
        fis.close();
    }
}

3.文件赋值(字节)

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Java_Io_02 {
    public static void main(String[] args) throws IOException {
        //首先输入到内存,即用输入类
        FileInputStream fis=new FileInputStream("C:\\Users\\邹飞鸣\\Desktop\\学习资料\\2020.7.14(暑假)\\markdown学习笔记\\javaSE笔记\\JavaSE基础语法笔记.md");
        //其次输出到硬盘,即用输出类
        FileOutputStream fos=new FileOutputStream("javaIO流Out类测试文件.md");
        int i;
        byte[] bytes=new byte[1024];
        //i返回的是有效文件个数
        while ((i=fis.read(bytes))!=-1){
            fos.write(bytes,0,i);
        }
        //先关文件输出流
        fos.close();
        fis.close();
    }
}

三、字符(Char)

1.输入流Reader

共性成员方法:

  • int read() 读一个字符
  • int read(char[] cbuf) 将字符读入数组。
  • abstract int read(char[] cbuf, int off, int len) 将字符读入数组的一部分。
  • abstract void close() 关闭流并释放与之相关联的任何系统资源。

2.FileReader输入流

  • 继承自InputStreamReader,InputStreamReader继承自Reader

构造方法:

  • FileReader(File file) 创建一个新的 FileReader ,给出 File读取。
  • FileReader(String fileName) 创建一个新的 FileReader ,给定要读取的文件的名称。
  • 作用:创建此类的对象并指向要读的文件

使用字符流读取文件中字符的步骤

  • 创建类的对象,使用read方法读取文件内容,方法返回值是int,接收并输出

代码示例:

import java.io.FileReader;
import java.io.IOException;
public class Java_Io_03 {
    public static void main(String[] args) throws IOException {
        FileReader fr=new FileReader("字符测试文件.txt");
        int len;
        while ((len=fr.read())!=-1){
            System.out.print((char)len);
        }
    }
}

读取多个字符:

import java.io.FileReader;
import java.io.IOException;
public class Java_Io_03 {
    public static void main(String[] args) throws IOException {
        FileReader fr=new FileReader("字符测试文件.txt");
        int len;
        char[] chars=new char[1024];
        //用字符数组装字符,并用String类中构造方法将字符转换成字符串
        //String(char[] value, int offset, int count) 分配一个新的 String ,其中包含字符数组参数的子阵列中的字符。 
        while ((len=fr.read(chars))!=-1){
            System.out.print(new String(chars,0,len));
        }
    }
}

3.字符输出流Writer

成员方法:

  • abstract void close() 关闭流,先刷新。
  • abstract void flush() 刷新流。
  • void write(char[] cbuf) 写入一个字符数组。
  • abstract void write(char[] cbuf, int off, int len) 写入字符数组的一部分。
  • void write(int c) 写一个字符。
  • void write(String str) 写一个字符串。
  • void write(String str, int off, int len) 写一个字符串的一部分。

4.FileWriter

  • FileWriter 继承 OutputStreamWriter 继承 Writer

构造方法:

  • FileWriter(File file) 给一个File对象构造一个FileWriter对象。
  • FileWriter(File file, boolean append) 给一个File对象构造一个FileWriter对象。
  • FileWriter(String fileName) 构造一个给定文件名的FileWriter对象。
  • FileWriter(String fileName, boolean append)构造一个FileWriter对象,给出一个带有布尔值的文件名,表示是否附加写入的数据。

文件写入步骤

  • 创建此类对象,构造方法写文件路径,调用write方法把数据写入到内存缓存区中(此间会将字符转换为字节),然后使用flush方法把内存缓冲区中的数据刷新到文件中,之后释放资源(释放资源也会把缓冲区数据刷新到文件中)
import java.io.FileWriter;
import java.io.IOException;
public class J04 {
    public static void main(String[] args) throws IOException {
        FileWriter fw=new FileWriter("字符测试文件.txt");
        fw.write("知识点");
        fw.flush();//刷新缓冲区数据到文件中
        fw.close();
    }
}
// 结果:
//     文件中显示:知识点

续写和换行

  • 续写:用有追加写开关的构造方法,即可,和字节续写相同,换行也相同。

四、缓冲流(增强流对象)

1.字节输出缓冲流(BufferedOutputStream)

  • BufferedOutputStream(OutputStream out) 继承 OutputStream
  • 相当于将要输入的全部字节存储到某个地方,一次性发送到指定的地方,使虚拟机和操作系统间交互尽可能少,以提高效率。
  • 构造方法:
    BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流
    BufferedOutputStream(OutputStream out,int size) 创建一个新的缓冲输出流,以将具有指定缓冲区大小的数据写入指定的底层输出流
  • 当传递一个输出流对象后缓冲流会给这个对象增加一个缓冲区,提高FileOutputStream的写入效率,size可以指定也能默认。

使用:
创建FileOutputStream对象,构造方法中绑定输出地址,然后将创建的对象作为参数创建BufferedOutputStream类的对象,使用缓冲流的对象对文件进行写入操作,然后刷新缓冲区(flush),然后释放资源()

import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class Io_06 {
    public static void main(String[] args) throws IOException {
        FileOutputStream fos=new FileOutputStream("缓冲流测试.txt");
        BufferedOutputStream bos=new BufferedOutputStream(fos);
        bos.write("测试缓冲流".getBytes());
        bos.flush();
        bos.close();
    }
}

2.字节输入缓冲流(BufferedInputStream)

  • 继承自InputStream

构造方法:

  • BufferedInputStream(InputStream in) 创建一个BufferedInputStream并保存其参数,输入流 in ,供以后使用。
  • BufferedInputStream(InputStream in, int size) 创建BufferedInputStream具有指定缓冲区大小,并保存其参数,输入流 in ,供以后使用。

代码示例:

import java.io.*;
public class Io_06 {
    public static void main(String[] args) throws IOException {
        FileInputStream fis=new FileInputStream("缓冲流测试.txt");
        BufferedInputStream bis=new BufferedInputStream(fis);
        //是显示字符串,所以先创建字节数组,将输入的字节存储到字节数组中,然后通过Sting的构造方法将字节数组转换为字符串输出
        int len;
        byte[] bytes=new byte[1024];
        while ((len=bis.read(bytes))!=-1){
            System.out.println(new String(bytes));
        }
        bis.close();
    }
}

3.字符缓冲输出流(BufferedWriter)

  • 继承自Writer

构造方法:

  • BufferedWriter(Writer out) 创建使用默认大小的输出缓冲区的缓冲字符输出流。
  • BufferedWriter(Writer out, int sz) 创建一个新的缓冲字符输出流,使用给定大小的输出缓冲区。

成员方法(特有):

  • void newLine() 写一行行分隔符。

实现代码:

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
public class Io_07 {
    public static void main(String[] args) throws IOException {
        FileWriter fw=new FileWriter("缓冲流测试文件.txt");
        BufferedWriter bw=new BufferedWriter(fw);
        bw.write("zdhd");
        bw.newLine();
        bw.write("shd");
        bw.flush();
        bw.close();
    }
}
// 结果:
//     zdhd
//     shd

4.字符缓冲输入流(BufferedReader)

  • 继承自Reader

构造方法:

  • BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流。
  • BufferedReader(Reader in, int sz) 创建使用指定大小的输入缓冲区的缓冲字符输入流

特有的成员方法

  • String readLine() 读一行文字。
  • 以换行符(‘\n’),回传(‘\r’)或者回车后直接跟着换行(\r\n)作为结束行的标志
  • 如果读取到的这一行无数据了,会返回null值,其他时候返回的是读取的数据(但是不会读取行的终止符合,即如果本来有换行,读取后是不会读取换行符)

代码示例:

import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
public class Io_08 {
    public static void main(String[] args) throws IOException {
        BufferedReader br=new BufferedReader(new FileReader("缓冲流测试文件.txt"));
        //读的是一行,所以只有一字符串为类型接收数据
        String str;
        while ((str=br.readLine())!=null){
            System.out.println(str);
        }
    }
}
// 结果:
//     zdhd
//     shd

五、对文件内容进行排序

文件内容:

1.测试缓冲流10
9.测试缓冲流2
2.测试缓冲流9
3.测试缓冲流8
6.测试缓冲流5
4.测试缓冲流7
5.测试缓冲流6
8.测试缓冲流3
10.测试缓冲流1
7.测试缓冲流4

排序代码:

import java.io.*;
import java.util.HashMap;
public class IO_00 {
    public static void main(String[] args) throws IOException {
        /*
        目的:将无序的10段话有序的录入进其他文件中
        1.使用字符缓冲输入流,获取文件中的数据。
        2.对数据进行切割,分离序号和文本内容
        3.对文件数据用HashMap集合进行存储,K为序号,V为文本内容
        4.遍历集合,将K,V值存储到指定文件
         */
        HashMap<String,String> hashMap=new HashMap<>();
        BufferedReader br=new BufferedReader(new FileReader("缓冲流测试.txt"));
        BufferedWriter bw=new BufferedWriter(new FileWriter("缓冲流测试排序.txt"));
        String str;
        //每次获取一行数据,返回值为null表示获取完成
        while ((str=br.readLine())!=null){
            //'.'是转义字符,前面必须加\\来表示后面的 . 是普通字符
            String[] strings=str.split("\\.");
            //将分割后的数据存储到集合中,K存储序号,V存储内容(HashMap会对数据按照K的数字大小进行排序)
            hashMap.put(strings[0],strings[1]);
        }
        //利用keySet方法将所有k值转换成set数组,并用增强for循环获取每个k值,然后利用get方法和k值获取每个v值
        for (String k:hashMap.keySet()) {
            //分割后,分割符会取消,所以需要手动添加
            bw.write(k+"."+hashMap.get(k));
            //换行,同样换行符也会取消,也需要手动添加
            bw.newLine();
        }
        br.close();
        bw.flush();
        bw.close();
    }
}

结果:

1.测试缓冲流10
2.测试缓冲流9
3.测试缓冲流8
4.测试缓冲流7
5.测试缓冲流6
6.测试缓冲流5
7.测试缓冲流4
8.测试缓冲流3
9.测试缓冲流2
10.测试缓冲流1

六、转换流

  • 作用:在文件进行写入或者读取操作时,文件是以字节形式存储在文件中,然后通过编码表对字节进行解码以呈现看得懂的文字,而就在在编码中,一般情况默认使用UTF-8编码,但是有的会使用GBK编码,如果编码录入,与解码读取所用的编码表不同,则会出现乱码情况,此时就可以使用转换流指定使用什么编码表进行读取或者写入操作。
  • OutputStreamWriter继承自Writer
  • InputStreamReader继承自Reader

构造方法:

  • 输出的: OutputStreamWriter(OutputStream out)创建一个使用默认字符编码的OutputStreamWriter。 OutputStreamWriter(OutputStream out, String charsetName) 创建一个使用命名字符集的OutputStreamWriter。
  • 输入的: InputStreamReader(InputStream in) 创建一个使用默认字符集的InputStreamReader 。InputStreamReader(InputStream in, String charsetName)创建一个使用命名字符集的InputStreamReader。
  • 参数:OutputStream,InputStream:分别是字节输出流和字节输入流; charsetName:编码名称,不区分大小写,默认为UTF-8

使用步骤:

  • 1.创建OutputStreamWriter对象,构造方法中new一个字节输出流,和指定编码。
  • 2.用OutputStreamWriter对象,调用方法写入数据,然后刷新缓冲区,释放资源
  • 3.创建InputStreamReader对象,构造方法new一个字节输入流,和指定编码。
  • 4.调用方法读取文件,释放资源。

实现代码:

import java.io.*;
public class Io_11 {
    public static void main(String[] args) throws IOException {
        OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("转换流测试.txt"),"gbk");
        osw.write("转换流");
        osw.flush();
        osw.close();
        InputStreamReader isr=new InputStreamReader(new FileInputStream("转换流测试.txt"),"gbk");
        int i;
        while ((i=isr.read())!=-1){
            System.out.print((char) i);
        }
        isr.close();
    }
}

七、序列化与反序列化

  • 定义:用来将对象进行存储,将对象利用字节方式存储进文件为序列化,将对象从文件中读取叫反序列化。

1.ObjectInputStream和ObjectOutputStream

构造方法:

  • 输出的:ObjectOutputStream(OutputStream out) 创建一个写入指定的OutputStream的ObjectOutputStream。参数是字节输出流
  • 输入的:ObjectInputStream(InputStream in) 创建从指定的InputStream读取的ObjectInputStream。

特有方法:

  • 输出的:void writeObject(Object obj) 将指定的对象写入ObjectOutputStream。
  • 输入的:Object readObject() 从ObjectInputStream读取一个对象。

使用步骤:

  • 1.先创建一个类及其对象a,并实现接口Serializable(不实现是会抛出异常的)
  • 2.再创建ObjectOutputStream对象,调用writeObject方法,参数是a(即第一步创建的对象),把对象写入文件中
  • 3.释放资源
  • 4.创建ObjectInputStream对象,调用readObject方法,将对象读取出来,除了io异常外还有一个关于class文件的异常,即需要有这个文件,读取的对象用类型为Object的变量接收。
  • 5.释放资源,打印对象。

实现代码:

import java.io.*;
public class Io_12 {
    public static void main(String[] args) throws IOException, ClassNotFoundException {
        A a=new A("邹飞鸣");
        ObjectOutputStream oos=new ObjectOutputStream(new FileOutputStream("序列化测试.txt"));
        oos.writeObject(a);
        oos.close();
        ObjectInputStream ois=new ObjectInputStream(new FileInputStream("序列化测试.txt"));
        Object obj=ois.readObject();
        ois.close();
        System.out.println(obj);
    }
}
//实现接口
class A implements Serializable{
    private String str;
    //定义有参构造方法一定还要定义一个无参的
    public A(){}
    public A(String str){
        this.str=str;
    }
    public String getStr() {
        return str;
    }
    public void setStr(String str) {
        this.str = str;
    }
//为了输出具体的数值 
    @Override
    public String toString() {
        return "A{" +
                "str='" + str + '\'' +
                '}';
    }
}
// 结果:
// A{str='邹飞鸣'}

2.反序列化序列号异常问题

当类实现Serializable后类会有一个序列号,这个序列号会随着对象进行序列化存储进文件中,当进行反序列化时会比较文件中的和类中的是否一致,如果一致才会读取,否则会抛出异常,而当类中代码发生改变后(如变量修饰符)序列号也是会改变的,而此时再读取已经被序列化的文件时,就会产生异常,所以可以将序列号固定不变,可防止此类问题:需在类中添加代码:

static final long serialVersionUID = 42L;

3.transient(瞬态关键字)

  • 作用,使修饰的变量不能够被序列化,即不能将赋好值的这个变量将值随着对象一起被存储进去。
  • static修饰后也不能被序列化。

后记

Java全栈学习路线可参考:【Java全栈学习路线】最全的Java学习路线及知识清单,Java自学方向指引,内含最全Java全栈学习技术清单~

本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到