系统运维系列 之IO流概述及其分类(java应用)

Posted 琅晓琳

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了系统运维系列 之IO流概述及其分类(java应用)相关的知识,希望对你有一定的参考价值。

1 前言
前面博客中已经介绍了各种编码问题,但是在I/O流传送的过程中,如果弄不清楚各种I/O流的异同,往往也会出现乱码的情况,在http(s)、socket、telnet等应用的过程中都要注意此类问题。以下为倾心整理的I/O流介绍,全是手打勿复制粘贴,力争成为一篇满满干货的博客,本文应该对I/O流的介绍和应用比较全面了。

2 主要内容
2.1 各种流:
字节流:字节流可以操作任何数据,因为计算机中数据以字节的形式存储;
字符流:只能操作纯字符数据,比较方便。
2.2 抽象父类:
字节流:InputStream、OutputStream
字符流:Reader、Writer
2.3 I/O流程序书写:
使用前导入I/O包中的类–>使用时异常捕获处理—>使用后释放资源
2.4 FileInputStream/FileOutputStream(访问文件):

//FileInputStream
//read()方法的返回值为什么是int?
//如果返回值为byte,当读取到byte的-1就会停止;如果用int类型接收,当遇到byte类型的-1时会补24个0,变成int类型的255,保证读取完数据。
public static void main(String[] args) throws IOException{
	FileInputStream fis = new FileInputStream("ABC.txt");
	int b;
	while((b = fis.read()) != -1){
		System.out.println(b);
	}
}
//FileOutputStream
//虽然写进去为int的数值,但是到文件中是一个字节,会自动去除前三个8位
public static void main(String[] args) throws IOException{
	//true代表可以追加
	FileOutputStream fos = new FileOutputStream("ABC.txt",true);
	fos.write(97);
	fos.close();
}

//字节数组拷贝之available()方法的应用(缺点:创建和文件一样大小的字节数组,会有内存溢出的风险)
public static void main(String[] args) throws IOException{
	FileInputStream fis = new FileInputStream("ABC.mp3");
	FileOutputStream fos = new FileOutputStream("copy.mp3");
	byte[]arr = new byte[fis.available()];
	//读取到内存
	fis.read(arr);
	//写到文件中
	fos.write(arr);
	fis.close();
	fos.close();
}
//解决方法一:定义小数组
public static void main(String[] args) throws IOException{
	FileInputStream fis = new FileInputStream("ABC.mp3");
	FileOutputStream fos = new FileOutputStream("copy.mp3");
	byte[]arr = new byte[1024*8];
	int len;
	//如果不加arr,返回的不是读取的字节个数,而是字节的编码值
	while((len = fis.read(arr)) != -1){
		fos.write(arr,0,len);
	}
	fis.close();
	fos.close();
}

2.5 BufferedInputStream/BufferedOutputStream(缓冲流 1024*8):

//接2.4 解决方法二:利用缓冲流
public static void main(String[] args) throws IOException{
	FileInputStream fis = new FileInputStream("ABC.mp3");
	FileOutputStream fos = new FileOutputStream("copy.mp3");
	BufferedInputStream bis = new BufferedInputStream(fis);
	BufferedOutputStream bos = new BufferedOutputStream(fos);
	int len;
	while((len = bis.read()) != -1){
		bos.write(len);
	}
	bis.close();
	bos.close();
}

//思考:
//小数组读写和Buffered读取哪个更快?
//如果定义小数组为8192个字节则小数组会略胜一筹,因为操作的是一个数组;而Buffered操作的是两个数组。
//flush和close的区别?
//flush方法:用来刷新缓冲区的,刷新后可以再次写出;
//close方法:用来关闭和释放资源,如果带Buffered对象的close方法,不但会关闭流,还会在关闭之前刷新缓冲区,关闭后不能再次写出。

2.6 FileReader/FileWriter(访问文件):
字节流直接操作字节,所以写出中文必须将字符串换成字节数组,写出回车换行,如:

write("\\r\\n".getBytes());

字符流读取字符,先要读取到字节数据,然后转为字符;如果要写出字符,需要先把字符转为字节再写出。
继承关系:
Reader—>InputStreamReader—>FileReader

//FileReader
public static void main(String[] args) throws IOException{
	FileReader fr = new FileReader("ABC.txt");
	int b;
	while((b = fr.read()) != -1){
		System.out.println((char)b);
	}
	fr.close();
}
//FileWriter
public static void main(String[] args) throws IOException{
	FileWriter fw = new FileWriter("ABC.txt");
	fw.write("大家好!");
	fw.close();
}
//字符流的拷贝
public static void main(String[] args) throws IOException{
	FileReader fr = new FileReader("ABC.txt");
	FileWriter fw = new FileWriter("copy.txt");
	int b;
	while((b = fr.read()) != -1){
		fw.write(b);
	}
	fr.close();
	fw.close();
}

//思考:
//什么情况下使用字符流?
//一般不推荐使用,因为读取时会把字节转为字符,写出时还要把字符转为字节;它的使用场景是需要读取一段文本,或者需要写出一段文本可以使用字符流。
//字符流是否可以拷贝非纯文本的文件(如视频)?
//不可以。因为在读取时会把字节转化为字符,在转化过程中可能找不到对应的字符,会用?代替;写出时把字符转化为字节用?就会解码不出来。

2.6 BufferedReader/BufferedWriter(缓冲流 102482):
BufferedReader中的read方法会一次性读取若干个字符到缓冲区;
BufferedWriter中的write方法写出字符时会先写到缓冲区。
2.7 readLine()/newLine()方法:
BufferedReader中的readLine方法可以读取一行字符(不包括换行符号);
BufferedWriter中的newLine方法可以输出一个跨平台的换行符号"\\r\\n"。

public static void main(String[] args) throws IOException{
	BufferedReader br = new BufferedReader(new FileReader("ABC.txt"));
	BufferedWriter bw = new BufferedWriter(new FileWriter("copy.txt"));
	String line;
	while((line = br.readLine()) != null){
		bw.write(line);
		//仅支持Windows
		bw.write("\\r\\n");
		//跨平台均可
		bw.newLine();
	}
	br.close();
	bw.close();
}

3 使用指定码表读写字符
utf-8 1中文3字节 GBK 1中文2字节

public static void main(String[] args) throws IOException{
	//InputStreamReader是字符流编码表
	//它是字节通向字符的桥梁,指定码表将字节--->字符
	BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream("ABC.txt","utf-8")));
	//OutputStreamReader是字节流编码表
	//它是字符通向字节的桥梁,指定码表将字符--->字节
	BufferedWriter bw = new BufferedWriter(new OutputStreamReader(new FileOutputStream("ABC.txt","gbk")));
}

//举例:
//Socket通信中文乱码解决--->对缓冲区的读取和写入使用如下代码
PrintWriter pw = new PrintWriter(new OutputStreamWriter(client.getOutputStream(),"UTF-8"),true);
BufferedReader br = new BufferedReader(new InputStreamReader(client.getInputStream(),"UTF-8"));

//补充:PrintWriter和BufferedWriter
//PrintWriter:向文本输出流打印对象的格式化表示形式(Prints formatted representations of objects to a text-output stream);
//如果PrintWriter开启了自动刷新,那么当PrintWriter调用println或format方法时,输出流中的数据就会自动刷新出去;
//PrintWriter不但能接收字符流,也能接收字节流。
//BufferedWriter:向文本中写入字符输出流,缓冲各个字符从而提供单个字符,数组和字符串的高效写入;
//通过write()方法可以将获取到的字符输出,然后通过newLine()进行换行操作;
//BufferedWriter中的字符流必须通过调用flush方法才能将其刷出去,并且BufferedWriter只能对字符流进行操作,如果要对字节流操作,则需要使用BufferedInputStream。
//Socket编程中,尽量用PrintWriter取代BufferedWriter,下面是PrintWriter的优点:
//PrintWriter的println方法自动添加换行,BufferedWriter需要显示调用newLine方法;
//PrintWriter的方法不会抛异常;
//PrintWriter构造方法可指定参数,实现自动刷新缓存(autoflush)。
PrintWriter pw = new PrintWriter(new BufferedOutputStream(
    new FileOutputStream("ABC.txt","gbk")));
BufferedWriter bw = new BufferedWriter(new OutputStreamReader(new FileOutputStream("ABC.txt","gbk")));

4 I/O流体系总结如下图(参考网络)
在这里插入图片描述

以上是关于系统运维系列 之IO流概述及其分类(java应用)的主要内容,如果未能解决你的问题,请参考以下文章

系统运维系列 之堆栈理解(java应用)

系统运维系列 之异常抛出后代码执行问题(java应用)

系统运维系列 之List实现深拷贝(java应用)

系统运维系列 之实现Ftp上传下载文件(java应用)

系统运维系列 之Java中synchronized详解及应用

系统运维系列 之容器为空和为null的区别