【Java IO 学习总结】

部分参考:
https://www.cnblogs.com/oubo/archive/2012/01/06/2394638.html

Java IO 学习总结

Java流类图结构

在这里插入图片描述

Java流操作的相关类或接口
  • File – 文件类

    • RandomAccessFile – 随机存储文件类
  • InputStream – 字节输入流

  • OutputStream – 字节输出流

  • Reader – 字符输入流

  • Writer – 字符输出流

输入流 or 输出流

输入 与 输出,是以程序端的角度来看流向,从文件(磁盘)中读取数据到程序就是输入in,将数据从程序写出到文件就是out。

in <-- read
out --> write
InputStream 字节输入流

但 InputStream 是抽象类,读文件的方法(read)是抽象方法。需要其实现类。

FileInputStream

InputStream 有很多的子类,其中最常用的是文件流FileInputStream了,主要用来读取文件。

    File file = new File("MockData.txt");
            
    FileInputStream fis = new FileInputStream(file); 
    // FileInputStream fis = new FileInputStream("MockData.txt");

有两个构造器:

    FileInputStream(File file);  
    FileInputStream(String path);

读取的方法:

    int read();
    int read(byte[] b);
    int read(byte[] b, int off, int len);
  • int read()

调用一次读到一个数据字节
返回的int值就是读到的数据,如果已到达文件末尾,则返回 -1。

    FileInputStream fis = new FileInputStream(file);

    int i;
    while ((i = fis.read()) != -1){
        System.out.println((char)i);
    }
    
    fis.close();
  • int read(byte[] b)

调用一次本方法表示可以读取多个数据。
读到的内容保存传入的byte数组b中。
返回的是本次调用方法读到的数据字节个数。

   FileInputStream fis = new FileInputStream(file);

   byte[] bytes = new byte[1024]; // 长度 决定了每次循环中读取的字节长度。长度越小循环次数越多
   while (fis.read(bytes) != -1){
       String string = new String(bytes);

       System.out.println(string);
   }

    fis.close();

字节数组是按字节长度来拆分到每一次批次循环的,一个中文是两个数据字节,不能避免一个中文被拆到两个批次中,导致不能识别而间歇性乱码

听说�
��文�
�乱码

需要关闭输入流

fis.close();

已关闭的流不能再读取到任何内容。

OutputStream 字节输出流

同样 OutputStream 是抽象类,写文件的方法(write)是抽象方法。需要其实现类。

FileOutputStream

构造方法:

	FileOutputStream(File file); 
	FileOutputStream(String name);
	// append 表示追加(true) or 覆盖(false),不传时默认false-覆盖
	FileOutputStream(File file, boolean append); 
	FileOutputStream(String name, boolean append); 

方法:

 	void write(int b);		//	调用一次写入一个数据字节
	void write(byte[] b);  	//	调用一次,可以把一个byte数组中的数据写入
 	void write(byte[] b, int off, int len);	//调用一次,把b数组中的一部分数据写入

FileOutputStream在执行write时,如果文件不存在,会自动创建一个文件(但文件的路径必须存在的)

  • void write(int b)
    FileOutputStream fos = new FileOutputStream(file,true);

    // 会自动将int 值转为char作为一个字节写入
    fos.write(66); // 写到文件里的是 B 

    fos.close();
  • void write(byte[] b)
    FileOutputStream fos = new FileOutputStream(file,true);

    fos.write("66".getBytes()); // 写到文件里的才是 66 

    fos.close();

getBytes() 时默认编码为"UTF-8"

仍然要需要关闭输出流

InputStream 其他实现类
  • ObjectInputStream

反序列化,从文件中读取成一个java对象。

    ObjectInputStream ois =  new ObjectInputStream(new FileInputStream("test.txt"));
    
    byte[] read = (byte[]) ois.readObject();
    String s2 = new String(read); // 再解析成对象

ObjectInputStream 只是装饰器(装饰者模式),源码中可知只有一个public的构造器,需要借助一个已有的InputStream才能使用,如FileInputStream。

    public ObjectInputStream(InputStream in);
    
    protected ObjectInputStream();
  • ByteArrayInputStream 和 StringBufferInputStream

ByteArrayInputStream、StringBufferInputStream、FileInputStream是三种基本的介质流,它们分别从Byte数组、StringBuffer、和本地文件中读取数据。

  • PipedInputStream

PipedInputStream是从与其他线程共用的管道中读取数据。

  • FilterInputStream

    FilterInputStream 也是一个装饰器,只能通过构造器

    protected FilterInputStream(InputStream in)
    

    创建对象,且必须通过子类(受protected修饰):

    • DataInputStream :

      使用InputStream我们只能读取byte,DataInputStream使得我们可以直接从stream中读取java中的int,String等类型。

    • BufferInputStream:

      这个类提供了一个缓存来加速我们从字节输入流的读取。

OutputStream 其他实现类
  • ObjectOutputStream

ObjectOutputStream和所有FileOutputStream的子类都是装饰流。

  • ByteArrayOutputStream

ByteArrayOutputStream、FIleOutputStream是两种基本的介质,它们分别向Byte 数组,和本地文件中写入数据。

  • PipedOutputStream

PipedOutputStream是从与其他线程共用的管道中写入数据。

字符流和字节流

字节流以字节(8bit)为单位读取数据。读取中文的时候非常不方便,容易产生乱码。

字符流的由来:本质其实就是基于字节流。读取时,去查了指定的码表,而有了对字符进行高效操作的流对象。

字节流和字符流的区别:

  • 读写单位不同:字节流以字节为单位,字符流以字符为单位,根据码表映射字符,一次可能读多个字节。
  • 处理对象不同:字节流能处理所有类型的数据(如图片、视频等),而字符流只能处理字符类型的数据。
  • 结论:只要是处理纯文本数据,就优先考虑使用字符流。 除此之外都使用字节流。

Reader 字符输入流

InputStreamReader

首先看他有唯一个子类 FileReader

FileReader的构造方法就是new FileInputStream字节流,再调用父类InputStreamReader构造器得到inputStreamReader对象

    public FileReader(String fileName) {
        super(new FileInputStream(fileName));
    }

FileReader都是使用InputStreamReader的read方法,以char[]为单位读取,而FileInputStream的read是以byte[]为单位读取。

int read();

read(char cbuf[], int offset, int length);
//底层是 read(char[] var1, int var2, int var3);

Reader reader = new FileReader(file);
等效于
Reader isr = new InputStreamReader(new FileInputStream(file));

    FileReader reader = new FileReader(file); 

    char[] cbuf = new char[3];
    int len;
    while((len = reader.read(cbuf))!=-1){
        System.out.println(new String(cbuf,0,len));
    }
   Reader isr = new InputStreamReader(new FileInputStream(file));// 等效于 Reader reader = new FileReader(file)

    char[] cbuf = new char[3];
    int len;
    while((len = isr.read(cbuf))!=-1){
        System.out.println(new String(cbuf,0,len));
    }

    isr.close();

同样,我们发现InputStreamReader 的创建,也是基于一个FileInputStream,同ObjectInputStream一样,只是一个装饰器。它将字节流转变为字符流。
子类FileReader的存在就是代码上可以省略了这一步。

可以说,使用字符流都离不开InputStreamReader来将字节流转为字符流。

BufferedReader

Buffer:表示缓冲区的。之前的StringBuffer,缓冲区中的内容可以更改,可以提高效率。

如果想接收任意长度的数据,而且避免中文乱码的产生,就可以使用BufferedReader。

有两个构造器(其实一个)

public BufferedReader(Reader in, int sz)
    
public BufferedReader(Reader in) {
    this(in, defaultCharBufferSize);
}

基于一个Reader(如InputStreamReader)创建对象,说明 BufferedReader也只是一个装饰者。

除了继承自Reader的

read(char cbuf[])

覆写了Reader的

read();
read(char cbuf[], int off, int len)

自己提供了

// 一次性从缓冲区中读取一行
String readLine();
String readLine(boolean ignoreLF);
// 此时,没有任何长度限制,可以输入很多的内容,每次都以回车结束(只要是一行不管多长)。

// 不仅可以接收键盘输入,还可以将文件中的内容读取到缓冲区之中 然后调用readLine()方法将缓冲区中的全部内容转为字符串.

需要注意的是,如果从文件中读取的话readLine一次只能读取一行的数据。
可以发现:从文件中使用readLine()方法读取行内容时,会自动接着上次在流中的位置进行读取。

如果要全部读取文件的中的内容有如下两种方法:

方法一:使用StringBuffer类不停的连接readLine()从每次读取的一行内容,直至读取的为null为止。然后进行输出。

方法二:使用StringBuffer类不停的连接read()方法读取到的每一个数字转化后的字符。然后进行输出。

StringReader

就是方便将代码里的字符串创建输入流。

    String str = "Hello World";
    StringReader sr = new StringReader(str);

    int sc = sr.read(); // 读取单个字符,若到流末尾则返回-1
    System.out.println((char) sc);
    String str = "Hello World";        
    char[] chars = new char[1024];
    int num = sr.read(chars, 0, str.length()); // 读取len个字符到chars数组中,从chars数组中的下标off开始存储,返回实际存储的字符数
    System.out.println(new String(chars, 0, num));
CharArrayReader
  • CharArrayReader 通过字符数组直接创建输入流。
// 构造器
public CharArrayReader(char buf[])
  • 其他
输入流的使用总结
  1. FileInputStream (字节输入流)

    • FileInputStream
      如果需要从文件读取图片、视频等,和简单的读取文件不用考虑中文,使用最基础的字节输入流。
    FileInputStream fis = new FileInputStream(file);
    fis.read();
    
    • 其他实现类:
      读java对象就ObjectInputStream;从从Byte数组、StringBuffer就ByteArrayInputStream、StringBufferInputStream…;需要直接读成java类型就DataInputStream、期望带缓存的字节流就BufferedInputStream
  2. InputStreamReader (字符输入流)

    如果考虑中文乱码或其他原因要使用字符流,那就绕不开 InputStreamReader。

    • 代码简洁地读取文件,可以直接用 FileReader,它的本质也是 InputStreamReader
    Reader reader = new FileReader(file);   
    // 等效于  
    Reader isr = new InputStreamReader(new FileInputStream(file),"UTF-8"); // 不指定编码则默认UTF8
    
  3. BufferedReader

    如果想接收任意长度的数据,或按整行读取,而且避免中文乱码的产生,就可以使用 BufferedReader。

    // 套娃比较多了,但是对于读中文文件最爽
    Reader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file))); // 当然也能指定编码
    // 其实写法等效于
    Reader reader = new BufferedReader(new FileReader(file));
    
    
    • 其他实现类:
      直接从字符串就StringReader,从Char数组就CharArrayReader…

Writer 字符输出流

OutputStreamWriter

同样的,它也是字节输出流与字符输出流之间的桥梁。
也有唯一一个子类FileWriter,提供了更快速使用OutputStreamWriter的方式。

FileWriter 构造方法摘要:

public FileWriter(String fileName) throws IOException {
    super(new FileOutputStream(fileName));
}
public FileWriter(File file) throws IOException {
    super(new FileOutputStream(file));
}
// append 代表覆盖 or  追加 (用的就是 FileOutputStream的覆盖 or 追加)
public FileWriter(File file, boolean append) throws IOException {
    super(new FileOutputStream(file, append));
}

OutputStreamWriter 构造方法摘要:

OutputStreamWriter(OutputStream out) 
// 支持设置编码,而FileWriter默认使用UTF8
OutputStreamWriter(OutputStream out, Charset cs) 
OutputStreamWriter(OutputStream out, String charsetName) 

FileWriter 是 OutputStreamWriter子类,可快速创建出 OutputStreamWriter

Writer writer = new FileWriter(file);
// 等效于
Writer writer = new OutputStreamWriter(new FileOutputStream(file));
Writer writer = new FileWriter(file,true);
// 等效于
Writer writer = new OutputStreamWriter(new FileOutputStream(file,true));

同样有方法

void write(int);
void write(char[]);
void write(char[],int,int);
void write(String,int,int);
void write(String);

FileWriter都是使用OutputStreamWriter的write方法,方法签名和OutputStream也相似,但以char[]为单位读取,而FileOutputStream的write是以byte[]为单位读取。

Writer osw = new OutputStreamWriter(new FileOutputStream(file));

osw.write("宫廷玉液酒");
osw.close();

字符流对比字节流,还会有一个flush()方法,在close()中会自动flush()。

public void flush();

当一次写入数据很多时,可以在中途手动刷新提交数据。
没有flush(),也没有close()时,数据是不会成功写到文件的。

BufferedWriter

BufferedWriter通过字符数组来缓冲数据,当缓冲区满或者用户调用flush()函数时,它就会将缓冲区的数据写入到输出流中。

查看构造函数也是依赖一个字符流,装饰者。

// 构造函数
BufferedWriter(Writer out) 
BufferedWriter(Writer out, int sz) 
// 方法
void    close()                              // 关闭此流,但要先刷新它。
void    flush()                              // 刷新该流的缓冲。
void    newLine()                            // 写入一个换行符。
void    write(char[] cbuf, int off, int len) // 写入字符数组的某一部分。
void    write(int c)                         // 写入单个字符。
void    write(String s, int off, int len)    // 写入字符串的某一部分。

创建BufferedWriter:

    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file,true))); // 覆盖 or 追加-true

    for (int i = 0; i < 10; i++) {
        
        writer.write(StringRandomTest.charRandomFromInt()); // 生成6位随机字符串
            
        writer.newLine();  // 换行
    }

    writer.flush();
    writer.close();
输出流的使用总结

与输入流的使用总结类似,分别考虑(字节输出流、字符输出流、带缓冲字符输出流)

  1. FileOutputStream (字节输入流)

    • FileInputStream
      如果需要写入其他格式文件,和写入简单的文件而不用考虑中文,使用最基础的字节输出流。
  2. OutputStreamWriter (字符输出流)

    如果考虑中文乱码或其他原因要使用字符流,那就绕不开 OutputStreamWriter。

    • 代码简洁地写文件,可以直接用子类 FileWriter,它的本质也是 OutputStreamWriter
    Writer writer = new FileWriter(file);
    // 等效于
    Writer writer = new OutputStreamWriter(new FileOutputStream(file));
    
  3. BufferedWriter

    如果想使用缓冲,或按整行写入,而且避免中文乱码的产生,就可以使用 BufferedReader。

    // 套娃比较多了(装饰者模式)
    BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file,true)));
    // 其实写法等效于
    Writer writer = new BufferedWriter(new FileWriter(file,true));
    
    
  4. 其他实现类的用法参考对比输入流其他实现类。

总结

输入流核心的类:FileIntputStream、IntputStreamReader
输出流核心的类:FileOutputStream、OutputStreamWriter

整理后的IO类结构

在这里插入图片描述

字符流与字节流使用(装饰者模式)

在这里插入图片描述

字节流和字符流区别

  1. 操作的单位不一样,一个是字节,一个是字符

  2. 操作中文的时候使用字符流更方便, 字节流更广泛:文本,视频,音频,图片…

  3. 字符流中有可以直接写字符串的方法

  4. 字节输出流 :

    程序 —> 磁盘文件 如果不关闭流也会写入

    字符输出流 :

    程序 —> 缓冲 —> 磁盘文件 如果不关闭流或者刷新缓冲区,不会写入文件

字符输出流,关闭的时候会先刷新,关闭之后不能够在操作,刷新之后可以继续操作。

刷新 : 写入的数据比较多时,可以在中途手动刷新提交数据。

输入流Demo

FileInputStream
	FileInputStream fis = new FileInputStream(file);

    /* int i;
      while ((i = fis.read()) != -1){
           System.out.println((char)i);
       }*/

 	byte[] bytes = new byte[5];
    while (fis.read(bytes) != -1){
        String string = new String(bytes);

        System.out.println(string);
    }
FileReader、InputStreamReader
// InputStreamReader isr = new InputStreamReader(new FileInputStream(file));
	FileReader reader = new FileReader(file);

	char[] cbuf = new char[3];
    int len;
    while((len = reader.read(cbuf))!=-1){
        System.out.println(new String(cbuf,0,len));
    }
BufferedReader
  • readLine 按行读取
	// BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file)));
	BufferedReader reader = new BufferedReader(new FileReader(file));
	
	String line = null;
	StringBuilder builder = new StringBuilder();
	while ((line = reader.readLine()) != null) {
		builder.append(line);
	}
  • 从当前项目resource下读取文件
     ClassPathResource classPathResource = new ClassPathResource("application.properties");
     InputStream inputStream = classPathResource.getInputStream();

     BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));

     String line = null;
     StringBuilder builder = new StringBuilder();
     while ((line = reader.readLine()) != null) {
         builder.append(line);
     }

输出流Demo

FileOutputStream
	FileOutputStream fos = new FileOutputStream(file,true);
	fos.write("每次一字节写入,中文还不会乱码".getBytes());

	fos.write("中文会乱码".getBytes(),0,10); // 指定长度就可能乱码了

	fos.close(); // 不关也会写入,建议手动关
FileWriter 、OutputStreamWriter
	// OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream(file,true));
	 FileWriter writer = new FileWriter(file,true);
     writer.write("直接写字符");
     writer.write("中文不乱码",0,5);

	// 一定要close或flush的时候才能将内容写到磁盘
    writer.flush();
    writer.close(); // 兼容一次flush
BufferedWriter
	// BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(file,true)));
    BufferedWriter writer = new BufferedWriter(new FileWriter(file,true));
    for (int i = 0; i < 10; i++) {
        for (int j = 0; j < 2; j++) {
            writer.write(charRandomFromInt()); // 6 位随机字符
            writer.write(",");
        }
        writer.write(charRandomFromInt());
        writer.newLine();  // 换行
    }
    
    writer.flush();
    writer.close();

其他Demo

文件拷贝

使用输入流读到内存,再用输出流写到磁盘,就完成了文件拷贝。

	/*每次读取到数组中,并且从数组中写入到文件,边读边写*/              
	FileInputStream fis = new FileInputStream(src);
	FileOutputStream fos = new FileOutputStream(dest);
	byte[] b = new byte[1024];
	int len;
	while((len = fis.read(b)) != -1){
		fos.write(b,0,len);
	}

IO流操作一般都应该关闭;
Java7起实现了AutoCloseable的IO流支持自动关闭(一般我们用到的IO流都是有实现此接口的)

从终端读取
	// System.in 就是一个InputStream
	InputStreamReader isr = new InputStreamReader(System.in);
	BufferedReader reader = new BufferedReader(isr);
	String line = reader.readLine();
	System.out.println(line);
	
	// 或者
	Scanner sc = new Scanner(System.in);
	String line = sc.nextLine();
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值