IO流技术-第一阶段-第十九天

本文详细介绍了Java中的IO流概念,包括输入流、输出流、字节流、字符流及其分类。通过实例展示了FileInputStream和FileOutputStream的使用,以及如何结合使用实现文件复制。还探讨了BufferedInputStream和BufferedOutputStream提高读写效率的方法,以及对象流ObjectInputStream和ObjectOutputStream在序列化和反序列化中的作用。最后,提到了字符流FileReader和FileWriter,以及BufferedReader和BufferedWriter在读写文本文件时的便捷性。

IO流

一、流的概念

流是指内存与存储设备之间传输数据的通道。
其中I 表示input 输入 O 表示output 输出,这里的入和出是以内存(JAVA程序为参照),要实现JAVA程序和外部磁盘文件之间的数据交互,首先需要在这两者之间建立一个数据的通信管道,然后让我们的数据在管道中进行流通,因此我们将这样的技术形象的称为“流技术”,如果是从JAVA程序到外部磁盘称为输出或者写操作,如果是从外部磁盘向JAVA程序中流动则称为输入或者读操作

二、流的分类

按方向:以内存为参照物

  • 输入流,指从存储设备输入到内存(读文件)
  • 输出流,指从内存输出到存储设备(写文件)

按单位:

  • 字节流,以字节为单位来处理所有文件
  • 字符流,以字符为单位来处理文本文件

按功能:

  • 节点流,普通的文件处理
  • 过滤流,增强型的文件处理(读文件每次读取一行)

按构造也能分为:基本流和包装流

三、IO流的使用

流的四大家族(流的四个顶级父类)
InputStream (字节输入流)
OutputStream (字节输出流)
Reader (字符输入流)
Writer(字符输出流)

FileInputStream 文件字节输入流
FileOutputStream 文件字节输出流
字节过滤流,对原来的字节流添加了一个缓冲,操作与原来几乎一样
BufferedInputStream 字节过滤输入流
BufferedOutputStream 字节过滤输出流
ObjectInputStream 对象输入流 反序列化
ObjectOutputStream 对象输出流 序列化
FileReader字符输入流
FileWriter字符输出流
BufferedReader缓冲字符输入流(包装流)
BufferedWriter缓冲字符输出流(包装流)
InputStreamReader转换字符输入流(可指定编码类型)
OutputStreamWriter转换字符输出流(可指定编码类型)

1. FileInputStream和FileOutputStream的使用

1.1 FileInputStream的使用

以字节为单位获取一个文件的内容
文件流使用完成后一定要进行手动的关闭,所有的流对象都统一实现了Closeable接口,
表示所有的流对象都是可以被关闭的,为了保证流的关闭代码无论是否发生异常都能被执行,要求将关闭的代码写在finally块中

代码举例

package com.qianfeng.day18;

import java.io.FileInputStream;
import java.io.IOException;

public class Work03 {
	public static void main(String[] args) {
		FileInputStream fis1 = null;
		FileInputStream fis2 = null;
		FileInputStream fis3 = null;
		try {
			//通过判断字节长度输出
			fis1 = new FileInputStream("D:\\Hello.txt");
			int i = 0;
			int max = fis1.available();
			while(i<max) {
				System.out.print((char)fis1.read());
				i++;
			}
			System.out.println("\n=========================");
			//通过判断是否到达文件最后一位输出
			fis2 = new FileInputStream("D:\\Hello.txt");
			String content = "";
			int r;
			while((r = fis2.read()) != -1) {
				content = content + (char)r;
			}
			System.out.println(content);
			System.out.println("=========================");
			//通过数组多次读取输出
			fis3 = new FileInputStream("D:\\Hello.txt");
			byte[] temp = new byte[5];
			int x;
			while((x = fis3.read(temp)) >= 0) {
				System.out.print(new String(temp,0,x));
			}
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			//手动回收IO流
			try {
				if(fis1 != null) {
					fis1.close();
				}
				if(fis2 != null) {
					fis2.close();
				}
				if(fis3 != null) {
					fis3.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}
1.2 FileOutputStream的使用

以字节为单位进行数据的输出(将程序中产生的数据保存到外部磁盘文件中)
字符串转字节数组:调用字符串对象的getBytes()即可得到该字符串对应的字节数组
默认在写入文件内容的时候采取的是覆盖的方式,如果希望以追加的方式写入则需要在构造输出流对象时指定第二个参数append为true
所有输出流都实现了Flushable接口,表示输出流都是可以被刷新的,该方法主要是用来避免写入的内容不完整
注意:在FileOutputStream创建对象的时候,如果没有将第二个参数设置为true那么会删除文件之前内容

代码举例

package com.qianfeng.day19;

import java.io.FileOutputStream;
import java.io.IOException;

public class Test01 {
	public static void main(String[] args) {
		FileOutputStream fos = null;
		
		try {
			String str = "qianfeng";
			fos = new FileOutputStream("D:\\hello2.txt");
			fos.write(str.getBytes());
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			if(fos != null) {
				try {
					fos.close();
				} catch (IOException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
		}
	}
}

1.3 FileInputStream和FileOutputStream结合使用

字节流也成为是“万能流”,可以实现对任何格式的文件进行读写操作,因为任何类型的文件最终都是以字节保存在磁盘上
这个例子以图片为例

代码举例

package com.qianfeng.day19;

import java.io.FileInputStream;
import java.io.FileOutputStream;

public class Test02 {
	public static void main(String[] args) {
		try(FileInputStream fis = new FileInputStream("D:\\test.png");
			FileOutputStream fos = new FileOutputStream("D:\\aaa\\hell.png",true);) {//true表示追加数据而不是覆盖数据
			int r;
			while((r = fis.read()) != -1) {
				fos.write(r);
			}
			fos.flush();
			System.out.println("复制完成");
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
	}
}

上述之所以没有回收是因为
将流的定义和创建代码写在try(流对象的定义和创建)默认在使用完成后自动进行关闭的操作,让我们的代码更加的精简

2 BufferedInputStream和BufferedOutputStream的使用

字节过滤流,对原来的字节流添加了一个缓冲,操作与原来几乎一样

代码举例

package com.qianfeng.day19;

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

public class Work01 {
	public static void main(String[] args) {
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		try {
			bis = new BufferedInputStream(new FileInputStream("D:\\鬼刀壁纸\\1111062.png"));
			bos = new BufferedOutputStream(new FileOutputStream("D:\\aaa\\test.png"));
//			int i;
//			long l1 = System.currentTimeMillis();
//			//读取每一位字节
//			while((i = bis.read()) != -1) {
//				bos.write(i);
//			}
//			bos.flush();
//			long l2 = System.currentTimeMillis();
//			System.out.println("方法1复制成功");
			int r;
			byte[] rs = new byte[10];
			long t1 = System.currentTimeMillis();
			//通过字节数组的方式复制
			while((r = bis.read(rs)) != -1) {
				bos.write(rs,0,r);
			}
			//刷新一下管道
			bos.flush();
			long t2 = System.currentTimeMillis();
			System.out.println("方法2复制成功");
//			System.out.println("方法1 耗时:" + (l2-l1));
			System.out.println("方法2 耗时:" + (t2-t1));
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if(bis != null) {
					bis.close();
				}
				if(bos != null) {
					bos.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

3、ObjectInputStream和ObjectOutputStream以及序列化

要将对象保存到文件中,或者将文件读取成对象,此时就应该使用对象流。

注意:使用对象流时,操作的对象需要实现序列化接口。

序列化:规定当前类如何以字节的形式存储。

反序列化:规定当前类如何以字节的形式读取。

想要实现序列化或反序列化,只需要让类实现java.io.Serializable,

目的是规定当前类如何与字节的形式相互转换。

public class Student implements java.io.Serializable{
	// 序列化的版本编号
	// 在反序列化时,会先比较版本号,版本号一致的才能读取到对象
	private static final long serialVersionUID = 8412075815327964533L;
	
	private int id;
	private String name;
	private String sex;
	private int age;
	
	
	public Student() {
		super();
	}


	public Student(int id, String name, String sex, int age) {
		super();
		this.id = id;
		this.name = name;
		this.sex = sex;
		this.age = age;
	}


	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}


	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getSex() {
		return sex;
	}

	public void setSex(String sex) {
		this.sex = sex;
	}

	public int getAge() {
		return age;
	}

	public void setAge(int age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Student [id=" + id + ", name=" + name + ", sex=" + sex + ", age=" + age + "]";
	}
}
public class TestMain5 {
	// 对象流
	public static void main(String[] args) {
		// 将对象写入文件(先转成流)
		Student stu = new Student(1, "张三", "男", 20);
		try (
				ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("C:\\Users\\wangliang\\Desktop\\123.txt"));
				){
			oos.writeObject(stu);
			oos.flush();
		} catch (Exception e) {
			e.printStackTrace();
		}
		
		// 将对象从文件中读取到内存
		try (
				ObjectInputStream ois = new ObjectInputStream(new FileInputStream("C:\\Users\\wangliang\\Desktop\\123.txt"));
				){
			Student stu = (Student)ois.readObject();
			System.out.println(stu);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

序列化的版本号必须要一致,如果不一致,反序列化时会报错。

当反序列化时,如果属性比序列化前少,或者多,对反序列化的结果影响不大,只是相应属性中没有值。

经典面试题:

transient和volatile的作用?

transient用来修饰属性,表示该属性在序列化时忽略,不会转换成流,保存后没有该属性。

volatile用来修饰属性,表示该属性在多线程并发时保证该属性的可见性,并不能实现线程安全。

4、字符流的使用

4.1 FileReader和FileWriter

以字节的方式进行文本信息的读取不太方便,因为有些字符在存储的时候不是占一个字节的空间,因此java中为了能够更加方便的读写文本文件提供了专用的流---->字符输入输出流
字符输入输出流只能进行文本的读写操作

4.2 BufferedReader和BufferedWriter

自带缓冲区的字符输入输出流,属于包装流
BufferedReader不仅可以提交读取的效率,同时也非常方便,该流对象提供了一个以行为单位进行文本文件的读取readLine()

4.3 InputStreamReader和OutputStreamWriter

属于字符流,也是一种包装流(转换流)
常见的字符集:Ascii iso8859-1 gb2312 gbk gb18030 big5 utf-8…
一切乱码的根源在于信息编码和解码的格式(编码和解码采用的字符集)不一致
字符流在构建的时候是无法指定编码和解码的方式,只有字节流的创建过程中才能指定字符集

字符流使用的代码举例

package com.qianfeng.day19;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.UnsupportedEncodingException;

public class Work02 {
	public static void main(String[] args) {
		BufferedReader br = null;
		try {
			br = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\Hello.txt"),"utf-8"));
			String resultLine;
			StringBuffer result = new StringBuffer();
			String temp = "";
			//每次读取一行,且读到为空就停止读取
			while((resultLine = br.readLine()) != null) {
				//将文件内容统一存储在result变量种
				result.append(resultLine + "\r\n");
				temp = temp + (resultLine + "\r\n");
			}
//			System.out.println(result);
			System.out.println(temp);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} 
	}
}

package com.qianfeng.day19;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

public class Work03 {
	public static void main(String[] args) {
		BufferedReader br = null;
		BufferedWriter bw = null;
		try {
			//1.创建一个字节流
			//2.将字节流转换成对应的字符流
			//3.将转换得到的普通的字符流包装成带有缓冲方式的字符流
			br = new BufferedReader(new InputStreamReader(new FileInputStream("D:\\hello2.txt"),"utf-8"));
			bw = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("D:\\hello2.txt",true),"utf-8"));
			String results = "您好\r\n我叫java\r";
			bw.write(results);
			bw.flush();
			System.out.println("添加成功!内容为:");
			String resultLine;
			StringBuffer result = new StringBuffer();
			while((resultLine = br.readLine()) != null) {
				result.append(resultLine + "\r\n");
			}
			System.out.println(result);
		} catch (UnsupportedEncodingException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (FileNotFoundException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} finally {
			try {
				if(br != null) {
					br.close();
				}
				if(bw != null) {
					bw.close();
				}
			} catch (IOException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值