1.IO流用来解决设备间的数据传输问题。
2.IO流的分类
按流的方向:
输入流:读取数据
输出流:写出数据
按数据类型:
字节流
字节输入流:读取数据 InputStream
字节输出流:写出数据 OutputSteam
字符流
字符输入流:读取数据 Reader
字符输出流:写出数据 Writer
什么情况下使用哪种流呢?
如果数据所在的文件通过Windows自带的文件夹可以打开并且能够读懂里面的内容,就用字符流。其他用字节流
如果什么都不知道,就用字节流。
注意:InputSteam和OutputSteam都是抽象类(abseract),不能直接用,需要使用它们具体的子类。
其实现类主要有以下:
InputStream:FileInputStream, FilterInputStream,StringBufferInputStream 等
OutputStream: FileOutputStream, FilterOutputStream 等
注意:每种基类的子类都是以父类名作为后缀名
3. 字节输出流FileOutputSteam
构造函数:
public void FileOutputStream(File file) :创建一个指向该file文件的字节输出流对象
public void FileOutputStream(String name) :创建一个指向该路径的字节输出流对象
public void FileOutputStream(File file, boolean append) :创建一个指向该file文件的字节输出流对象,如果append值为true,则将字节写到文件末尾处,否则会覆盖原来的。
public void FileOutputStream(String name, boolean append) :创建一个指向该路径的字节输出流对象,如果append值为true,则将字节写到文件末尾处,否则会覆盖原来的。
写数据方法:
public void write(byte[] b) :写一个字节
public void write(int b) :写一个字节数组
public void write(byte[] b, int off, int len) :写一个字节数组的一部分
使用步骤:
A:创建字节输出流对象
FileOutputSteam fos = new FileOutputSteam(“fos.txt”) ;
此处字节输出流做了几件事情:
1)如果不存在该文件,则调用系统功能去创建该文件,如果存在则不做这件事
2)创建fos对象
3)把fos指向这个文件
B:写数据
fos.write( “Hello.IO”.getBytes() ) ;
此处用的方法是 void write(byte[] b) ; 可见参数是一个字节数组,因此要把字符串转换为字节数组。
C:释放资源
fos.close() ;
为什么一定要close()呢?
1)让流对象变成垃圾,这样就可以被垃圾回收器回收
2)通知系统去释放跟该文件相关的资源(这点很重要)
问题1:如何在写入的时候实现换行?
很简单,只需要在写入字节流的时候同时写入换行符即可。但是不同系统的换行符是不同的,
Windows:\r\n
Linux:\n
Mac:\n
而一些常见的高级记事本是可以识别任意换行符的。
使用IO流的过程中需要注意的事会需要加入异常处理,因此上述代码的最终完美版应该事下面这样:
FileOutputStream fos = null ; //这里不赋值为null的话close()方法会报错
try{
fos = new FileOutputStream("fos.txt") ; //如果是绝对路径,这里可能会出现找不到路径的情况,即FileNotFoundException异常
fos.write("hello.IO".getBytes()); //这里可能会报IOException的异常
}catch(FileNotFoundException e){
e.printStackTrace();
}catch (IOException e) {
e.printStackTrace();
}finally{ //为了防止上面两步出错导致字节流无法运行close()方法,把关闭字节流方法写到finally中即可。
if(fos != null) //如果第一步找不到路径的话fos就会为空,那么对象调用close()方法就会无意义,因此为了防止这种情况,加入一个判断
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
4、字节输入流FileInputStream
构造方法
public FileInputStream(File file) :
public FileInputStream(String name) :
读取方法:
int read():一次读取一个字节,读到文件结尾的话返回-1
int read(byte[] b):一次读取一个字节数组,返回的结果是实际读取到的数据的长度,读到文件结尾则返回-1
读取步骤:
A:创建字节输入流对象
FileInputStream fis = new FileInputStream (“fis.txt”) ;
B:调用read()方法读取数据,并返回该字节对应的int值
int b = fis.read() ;
C:释放资源
fis.close() ;
因为read()方法一次指读取一个字节,所以要用循环判断来读取。如下:
int b = 0 ;
while(int b = fis.read() != -1){
System.out.print((char) b) ;
}
注意:这里只能读取数字和英文字符,不能读取中文汉字,因为汉子占用两个字节,这样读取到的汉子都是乱码,因此才会出现字符流。
在计算机中中文存储用的两个字节,其中
第一个字节肯定是负数,
第二个字节常见的是负数,可能有负数,但是没影响。
但是,实际用中并不常用read()来进行单字节读取,通常是使用read(byte[] b)方法,这样的效率会比单字节读取速度提升字节数组的长度倍(理论上)。其最终版代码如下:
FileInputStream fis = new FileInputStream("fis.txt") ;
byte[] by = new byte[1024] ; //这里的字节数组长度通常设置为1024或者1024的倍数,理论上速度提高1024倍
int len = 0 ;
while( (len = fis.read(by))!= -1 ){
System.out.print(new String(by , 0 , len)); //这里一定要用0-len的长度输出,防止最后一次读取只能更新字节数组的前面一部分。
}
fis.close() ;
5. 由上我们可以看到设置一个数组作为缓冲区确实可以提高读写效率
既然这样的的话,Java在开始设计的时候也采用的这样的思想,提供了一种带缓冲区的字节类。这种类被称为缓冲区类(高效类)。
写数据:BufferOutputStream
构造方法:
BufferOutputStream(OutputStream out) :创建一个缓冲输出流,把数据写入指定的底层输出流
BufferOutputStream(OutputStream out , int size):创建一个指定大小的缓冲输出流,把数据写入指定的底层输出流
注意:为什么不直接指定具体的文件或者路径而是指定一个输出流对象呢?
因为字符缓冲流仅仅提供缓冲区,为高效设计的,但是真正的读写还是要靠基本的流对象来实现。
BufferOutputStream bos = new BufferOutputStream(new FileOutputStream("bos.txt")) ; //通常采用匿名对象方法
bos.write("hello".getBytes());
bos.close() ;
读数据:BufferInputStream
构造方法:
BufferInputStream(InputStream in) :创建一个缓冲输入流
BufferInputStream(InputStream in , int size):创建一个指定大小的缓冲输入流
BufferInputStream bis= new BufferInputStream(new BufferInputStream("bis.txt")) ;
byte[] by = new byte[1024] ; //这里的字节数组长度通常设置为1024或者1024的倍数,理论上速度提高1024倍
int len = 0 ;
while( (len = bis.read(by))!= -1 ){
System.out.print(new String(by , 0 , len)); //这里一定要用0-len的长度输出,防止最后一次读取只能更新字节数组的前面一部分。
}
fis.close() ;