看了IO两天的课程,对大体的一些概念做个总结。
1, Input,Output
什么时候用IO。
当对设备上数据进行操作的时候,就要用到IO包中的对象。
以数据流的形式存在。参阅IO包中的体系。
对于IO而言,操作无外乎读和写连个最常见操作。
IO按照流向分:输入流,输出流。
IO按照处理数据分:字节流,字符流。
2, 字节流可以处理一切数据,为什么还要出现字符流呢?
为了提高处理字符数据的效率,因为字符数据涉及到了编码转换。
流的基类。
字节流抽象基类:
InputStream,OutputStream。
字符流抽象基类:
Reader,Writer
3, 通常数据都以文件的形式存放在硬盘设备上。
所以先使用了对文件进行操作的流对象。
FileReader,FileWriter。
当查阅这两个流对象时,发现他们的构造函数都会接收一个文件。
因为在写入的时候,必须要先有一个数据存放的目的。所以FileWriter在进行
初始化的时候,立刻会定义数据存放目的。
FileWriter(String fileName)
FileWriter(String fileName,boolean append):可以文件进行续写。
同时在读取的时候,必须要明确要读取的数据存放的位置,也就是数据源,,
数据源通常是一个已存在的文件。
FileReader(String fileName) //文件不存在,会出现FileNotFoundException
A,字符写入流。
a,建立流对象,并明确数据要存放的目的。
/*做了两件事:
1,堆内存出现对象,
2,调用了底层资源创建流,并在指定位置创建文件。
如果在指定目录下已存在该文件,则被覆盖。为了可以在原有文件中进行
数据的续写。可以在构造函数中传入另一个boolean参数为true进行续写。
*/
FileWriter fw = new FileWriter("a.txt");
b,指挥对象做事情,调用了写入流对象写入方法,将数据写入流中。
fw.write("haha");
c,将流的数据刷新到目的地。
fw.flush();
d,当流使用完毕后,必须要做关闭资源的动作。
//关闭动作其实做了两件事,1,刷新,2,关闭。
fw.close();
这四句代码都会抛出异常IOException。
进行try catch处理。
FileWriter fw = null;
try
{
fw = new FileWriter("a.txt");
fw.write("ddfd");
fw.flush();
}
catch (IOException e)
{
System.out.println(e.toString());
}
finally
{
if(fw!=null)
try
{
fw.close();
}
catch (IOException e)
{
System.out.println(e.toString());
}
}
B,字符读取流。
a,建立读取流对象,并和要读取的源相关联.如果源不存在,会出现FileNotFoundException
FileReader fr = new FileReader("a.txt");
b,将数据取出到哪里呢?需要先定一个临时存储单位(字符数组)长度为1024或
1024的整数倍。
char[] arr = new char[1024];
c,使用读取流对象的read方法将读取到的数据存入到该数组当中。
//该read方法会返回一个int型的值,该值代表往数组存入字符的个数。
//该方法如果读到了文件末尾,会以-1作为结束标识返回。
int num = fr.read(arr);
c.1,数据有很多,需要调用多次read方法才可以完成所以需要使用循环结构.
int num = 0;
while((num=fr.read(arr))!=-1)
{
System.out.println(new String(arr,0,num));
}
d,数据取到数据进行操作,比如打印。
System.out.println(new String(arr,0,num));//将数组的有效字符进行取出打印。
e,关闭读取流。
fr.close();
在字符读取流中有一个空参数的read()方法,该方法返回的是:读到的单个字符。
扩展:为什么返回的是int,而不是char呢?
那是因为有特殊情况出现,如:读到的16个二进制位都是1,那么返回的就是-1.
FileReader fr = new FileReader("c:\\a.txt");
int ch = 0;
while((ch=fr.read())!=-1)
{
System.out.println((char)ch);
}
fr.close();
-----------------------------------------------
4, 字符缓冲区:
FileReader,FileWriter
BufferedReader,BufferedWriter
Buffer的出现是通过装饰设计模式而来的。
装饰设计模式。(包装设计模式)
解决问题:
对已有对象的功能进行增强。
问题是:
继承也可以实现类似的功能。那么包装和继承有什么区别呢?
Reader
|--FileReader
|--DataReader
为了丰富流的功能,有了可以操作文件的流对象,也有了也可以操作数据的流对象。
但是发现操作效率较低,为了提高操作效率,引入了缓冲技术。
Reader
|--FileReader
|--BufferedFileReader
|--DataReader
|--BufferedDataReader
|--MediaReader
|--BufferedMediaReader
Reader
|--FileReader
|--DataReader
|--MediaReader
|--BufferedReader
------------------------------------------------
5, 字节流:
FileInputStream ,FileOutputStream.
和字符流的操作思想是一致的,但是使用的缓冲区是字节数组。
注意:
字节流在使用时不使用close()和flush(),数据也可以写入到目的地,
因为字节是源数据,不需要进行编解码。
字节读取流中有一个特殊的方法:
available():可以获取关联的文件字节大小。
如果文件较小,可以直接通过该方法建立刚刚好字节数组缓冲区。不需要循环,
就可以完成读取,但是文件较大,容易出现内存溢出。
字节流的缓冲区。
BufferedInputStream , BufferedOutputStream.
同样,也是必须要和字节流相关联。
模拟一个BufferedInputStream
原理:
自定义字节读取流缓冲区,其实就是内部封装了一个字节数组。
将字节数组存满后,一个一个的取出。取完后再装一批数组,再取。
class MyBufferedInputStream
{
private InputStream in;
private byte buf = new byte[1024*8];
private int pos = 0,count = 0;
MyBufferedInputStream(InputStream in)
{
this.in = in;
}
public int myRead()
{
if(count==0)
{
count = in.read(buf);
if(count<0)
return -1;
pos = 0;
byte b = buf[pos++];
count--;
return b&0xff;
}
else if(count>0)
{
byte b = buf[pos++];
count--;
return b&0xff;
}
return -1;
}
}
---------------------------------------------------
常用的8个对象:
FileReader,FileWriter
BufferedReader , BufferedWriter
FileInputStream , FileOutputStream
BufferedInputStream , BufferedOutputStream
java.io
类 InputStreamReader