什么是I/O?
I/O无论对于哪一门语言都是非常重要的,因为通过它我们才能实现对文件读写操作,在实际开发中,我们常常需要通过应用程序对一些数据进行读或写的操作(这些数据可能来自于存储设备、网络、输入设备等),这些操作都是通过I/O实现的。
Java也为我们提供了一套较为完善的I/O,所谓I/O,简单粗暴点,就是通过它我们能够实现对数据的读或写。
I/O操作的目标是什么?
目标很明确,从数据源当中读取数据,以及将数据写入到数据目的地当中
这里需要注意一下,所谓的“数据源”不仅限于硬盘,它还可以是网络,输入设备,某个文件等等,同理,“数据目的地”也是多种多样的,包括某个文件,显示器,打印机,网络等等
I/O的分类
I/O可分为两类
1.输入流:有数据进入到程序中
2.输出流:数据从程序输入到数据目的地
这里需要强调的是,“流”的方向都是相对于应用程序本身的。(当然,也有双向的“流”,但是开发用的极少,就此略过!!!(#任性)(#任性))
什么是“流”?
对于初学者,当看到“输入流”,“输出流”的时候,可能会特别纳闷儿。。。“流”是个什么东东啊!!!有人说是按照英文“Stream”翻译过来的,哈哈哈哈,也可以这么解释嘎,但为什么英文要用“Stream”啊~!作为一名程序猿我们肯定要弄清楚事情的真相啊!
事情的是这样的,所谓“流”呢,其实就是指程序和数据源或数据目的地建立了一个数据流通的“管道”,数据的输入和输出,并不是Duang的一下就完成的,而是像水管中的水一样,通过“管道”一点儿一点儿的流向程序中或数据目的地
这就是“流”的真相。。。。。。
Java中I/O流
Java中的I/O流可分为两大类
1.字节流:在读取数据的时候是以字节为基础,一次读取一个或多个字节
2.字符流:在读取数据的时候是以字符为基础,一次读取一个或多个字符
下面是Java中的I/O流的概览图:
\'!@#>*&%?>"/......
是不是有种崩溃掉的感觉??啊哈哈哈哈哈!!!
看上去貌似很复杂的样子啊,其实嘛。。。,真的挺复杂的(#挖鼻屎)(#挖鼻屎),不过呢,复杂不代表不易学啊,其实Java中I/O,只要弄懂了其中一条,其他都是“万变不离其宗”了。
Java中的字节流
首先大家要记住,以“Stream”结尾的流都是字节流。上面已经说过了,字节流就是在读取数据的时候是以字节为基础,一次读取一个或多个字节
从上图中可以看到,Java中的字节流可分为两大类:
1.InputStream(字节输入流)
2.OutputStream(字节输出流)
它们的核心方法如下
(1)InputStream(字节输入流):
public int read(byte[] b,int off,int len)
这是一个读取数据的方法,读进来的数据会放到一个byte类型的数组里面。
<1>第一个参数表示一个byte类型的数组,用来存放读取的数据
<2>第二个参数表示偏移量,用来确定读取的数据从数组的第几位开始存放(一般都是0)
<3>第三个参数表示一次读取多少位数据(一般是数组的长度)
<4>返回值表示该方法一次读取了多少个字节的数据;当返回值为-1时,表示文件已经读取完毕
(2)OutputStream(字节输出流):
public void write(byte[] b,int off,int len)
这是一个写入数据的方法,先把要写入文件的数据转换成byte类型的数组,然后再从数组中读取数据写入文件中
<1>第一个参数表示一个byte类型的数组,用来存放写入文件的数据
<2>第二个参数表示偏移量,用来确定读取的数据从数组的第几位开始读取
<3>第三个参数表示一次写入多少位数据到文件中
强调一下,I/O中,读和写的操作是分开的。
InputStream和OutputStream是所有字节流的父类,它们都是抽象类,抽象了应用程序读取数据的方式。既然是抽象类,我们就不能直接创建他们的对象了,只能通过它们的子类来创建对象,然后再进一步操作。
但是,你会发现它的子类相当多啊。。。不用担心,其实呢,我们常用的类就那么几个,把最常用的弄懂了,其他的你需要使用时,看看随便看看文档就会一目了然了。
下面通过一个小例子来帮大家理解如何通过字节流来对文件进行读和写的操作,我们要把F盘下的from.txt中的数据读取并写入到了to.txt文件中。为了帮助大家理解,我还是先用一张图来展示整个过程是怎么实现的:
其中,Byte类型的数组充当了一个缓冲数据的角色。整个过程是一个边读边写的过程。
下面在代码中具体实现:
import java.io.*;
public class TestByte{
public static void main (String args[]){
FileInputStream fis = null ;
FileOutputStream fos = null ;
try {
fis = new FileInputStream("F:/from.txt" );
fos = new FileOutputStream("F:/to.txt" );
byte [] buffer = new byte [1024 ];
while (true ){
int temp = fis.read(buffer,0 ,buffer.length);
if (temp == -1 ){
break ;
}
fos.write(buffer,0 ,temp);
System.out .println(temp);
}
}
catch (Exception e){
System.out .println(e);
}
finally {
try {
fis.close();
fos.close();
}
catch (Exception e){
System.out .println(e);
}
}
}
}
这里需要补充几点:
(1)当我们使用IO流完毕后,必须调用close()来关闭对应的流(聪明的人都会在finally中调用,哈哈哈哈),否则应用程序可能会产生意想不到的错误!!!
(2)FileOutputStream的构造方法有两种形式:
FileOutputStream fos = new FileOutputStream(File f/String s);
如果该文件不存在,则直接创建;如果存在,删除后重新创建。
FileOutputStream fos = new FileOutputStream(File f/String s,ture);
如果该文件不存在,则直接创建;如果存在,则在基础上追加内容。
Java中的字符流
Java中,以“Reader”和“Writer”结尾的流都是字符流,所谓字符流,就是在读取数据的时候是以字符为基础,一次读取一个或多个字符
字符流可以分为两大类:
1.Reader(字符输入流)
2.Writer(字符输出流)
Reader和Writer都是所有字符流的父类,并且他们都是抽象类,不能直接生成对象,只能通过其子类来创建对象,再进一步操作。它们的核心方法和字节流类似,只是字符流是通过char类型的数组来保存读取的数据,如下
(1)Reader(字符输入流):
public int read(char[] c, int off ,int len)
这是一个读取数据的方法,读进来的数据会放到一个char类型的数组里面。
<1>第一个参数表示一个char类型的数组,用来存放读取的数据
<2>第二个参数表示偏移量,用来确定读取的数据从数组的第几位开始存放(一般都是0)
<3>第三个参数表示一次读取多少位数据(一般是数组的长度)
<4>返回值表示该方法一次读取了多少个字节的数据;当返回值为-1时,表示文件已经读取毕
(2)Writer(字符输出流):
public void write(char[] c, int off ,int len)
这是一个写入数据的方法,先把要写入文件的数据转换成char类型的数组,然后再从数组中读取数据写入文件中
<1>第一个参数表示一个char类型的数组,用来存放写入文件的数据
<2>第二个参数表示偏移量,用来确定读取的数据从数组的第几位开始读取
<3>第三个参数表示一次写入多少位数据到文件中
值得一提的是,字符输出流在将数据写到数据目的地之前,会先把数据保存到缓冲区(一个特殊的内存区域)中,这样有利于在重复使用数据时字节从内存中写入,从而提高应用程序运行的效率。
下面还是通过一张图来帮助大家理解字符流的工作原理:
代码具体实现:
import java.io.*;
public class TestChar{
public static void main (String[] args){
FileReader fr = null ;
FileWriter fw = null ;
try {
fr = new FileReader("F:/from.txt" );
fw = new FileWriter("F:/to.txt" );
char [] buffer = new char [100 ];
while (true ){
int temp = fr.read(buffer,0 ,buffer.length);
if (temp == -1 ){
break ;
}
fw.write(buffer,0 ,temp)
}
}
catch (Exception e){
System.out .println(e);
}
finally {
try {
fr.close();
fw.close();
}
catch (Exception e){
System.out .println(e);
}
}
}
}
最后不要忘了在finally中调用close()来关闭流,切记切记!!
字节流和字符流的对比和总结
(1)使用字节流还是字符流?
所有的文件在硬盘或在传输时都是以字节的方式进行的,包括图片等都是按字节的方式存储的,而字符是只有在内存中才会形成,所以在开发中,字节流使用较为广泛。
(2)字节流和字符流在处理数据是有什么不同?
字节流在操作数据的时候是不会用到缓冲区(内存)的,它直接对文件本身直接操作的,即使不关闭流(调用close方法),文件也能输出(不推荐这样做),而字符流在操作的数据的时候,会先将数据缓存到缓冲区,所以字符流不关闭流(调用close方法),则不会输出任何内容,但可以使用flush方法强制进行刷新缓冲区,这时才能在不close的情况下输出内容
(3)字节流和字符流之间有什么联系吗?
字节流和字符流的相互转化,底层实际上是通过public String(byte bytes[], String charsetName)将byte类型的数组转化为String字符串,或是,通过String.getBytes(String charsetName)将String类型的字符串转化成byte类型的数组来实现的。但是转化过程要注意编码是否一致。
好了,写到这里,相信你对Java的IO流中字节流和字符流常用类的使用应该多少所有掌握了,在本篇博客中,我把Java中IO流分为了“字节流”和“字符流”,但更深层次的我们还可以把Java中I/O流分为“节点流”和“处理流”,这就涉及到Java中的一种设计模式————工厂模式,Java中的IO流实际上就是一个工厂模式实现的,如果掌握了这种思想,那么你对Java中的I/O将会有更加深入的领悟。我将在[Java文件操作(三)——浅谈Java中I/O(下)](http://blog.csdn.net/qq_24789865/article/details/48879657)中具体介绍JavaI/O中的“节点流”和“处理流,以及工厂模式的思想。