----------------------class="Apple-converted-space">?android开发、java培训、期待与您交流! ----------------------
?
IO:主要用来处理
设备之间的数据传输(如
硬盘上的文件,
内存中的数据),java对数据的操作是通过流的方式,
java操作流的对象存在IO包中,流按数据分为两种,字节流和字符流。
?字符流的数据字节流其实都能处理,因为无论什么在计算机上最后都是以字节存在的,但是其中有一部
?分数据它是以文本形式存在的,所以为了方便有了字符流,字符流里
融合了
编码表,而只有文字用到了
?编码表,所以字符流用来处理文本形式的数据。
?
按方向分的话有输入流和输出流之分。
字符流: ?
??
??
??
??
??
??字节流:
?Writer ?
?Reader ?
??InputStream ?OutputStream?
这四个基类的子类名称都是以父类的名称为后缀,如FileReader,FileInputStream,
在写有关IO流程序的时候,首先要注意:
?1.要注意一定要记得导包,import java.Io.*;
?2.记得要进行
异常处理,和IO流有关的都会抛异常,原因:例如FileWrite("C:\\abc.txt");
?
??因为括号里的
文件名会有可能出错,比如写成(K:\\abc.txt)
?3.最后一定要进行关闭流的操作。因为它底层调用的其实是window资源,所以要关闭。
?
---------------------------------------------------------------------------------------------
?
下面是IO
异常处理方式:
例如;
?
FileWriter fw = null;//2.所以一般在外面定义引用在try内初始化,这样fw就作用于整个程序中,
try
{
//1.因为会抛异常所以要进行try处理,但是如过把下面这句放在try代码块里面定义fw,那么
//在别的代码块里会访问不到(比如finaly里关流的时候)
//FileWriter fw = new FileWriter(c:\\abc.txt);
fw = new FileWriter(c:\\abc.txt); // 代码1
fw.Write("abcdef"); //代码2
}
catch(IOException e),
{
//异常处理
}
finaly
{
try
{//一定要对关闭的流对象进行判断是否为空
if(fw != null)//写这句的原因:因为如果“代码1”处出现异常被处理了,那么代码里面fw里面也就
//没有被写进东西,所以fw为空。
fw.close();//如果为空的话则不能调用这句,所以要进行下是否为空判断
}
catch(IOException e),
{
//异常处理
}
}
??
?
字符流:writer(写)
?根据以往经验,要想了解一个体系,是不是都要先从最顶端的开始了解啊?字符流写的方法最顶端的
?父类就是writer,它里面有append(),close(), write() 等方法,可以知道它的子类中肯定也都具有
?这些功能。既然是字符流,那么它操作的最基本数据就是字符, FileWriter 里的主力方法就是write
?方法,就是往文件里写数据的方法,既然是操作字符了,那肯定忘里写单个字符啊字符数组啊,字符串
?啊都行。
?
?字符流---创建文件 ?异常处理如上:
?
?FileWriter fw = new FileWriter("Demo.txt");// ?在此目录下创建一文件,如果已经存在会被覆盖掉。
?FileWriter fw = new FileWriter("Demo.txt",true);//不覆盖原文件,在末尾处添加
?fw.write("abcdefg");//调用write()方法,其实是存在了流(内存)中
?fw.flush();//将流对象中缓冲区里的刷新数据至目的地中
?fw.close();// close()特点:刷新一次数据然后关闭流
?
?其实,我们不用java也能创建一个文件往里写数据,也就是说windows系统本身就具有这个动作,java能往
?windows的文件系统中写入数据,是不是代表着java在调用win
dos中那个写的动作?这个就是我们所说的流
?资源。其实java本身是不能往硬盘里写数据的,我们知道windows和linux系统它们写数据的方式是不是不一
?样啊?所以java得靠系统本身的方式来完成对数据的书写。总的来说就是java写数据什么的是不是在调用
?windows的系统资源用完了要怎么办?是不是要释放出来,所以close动作是一定要做的。
?
?文件的续写:
?
?建个文件往里面写数据,但是那个文件里已经有数据了该怎么往里续写数据呢?写一次刷新一次,通过
?
?查看API我们
发现FileWriter类有一种
构造函数
?
?FileWriter(String fileName, boolean append)
?
?参数: fileName - 一个字符串,表示与系统有关的文件名。
?
??append - 一个 boolean 值,如果为 true,则将数据写入文件末尾处,而不是写入文件开始处。?
---------------------------------------------------------------------------------------------
?
字符流 Reader(读)
?说完写该说读了,查看API发现Reader类里也有很多和Writer方法,如close()等,但是它里面没有flush();
?另外它里面有一群读的方法(read),如读单个字符,或把数据读入到一个数组,另外读文件的话是不是先把
?文件传给他啊,所以在
创建对象的时
候要把指定文件与它相关联。如果不存在会发生文件未找到异常。
?
?字符流---读取文件1 下面是没有进行异常处理的
?
?FileReader fr = new FileReader("Demo.txt");//文件不存在会抛异常
?int x = fr.read();//一次读一个会自动往下读,如果在调用一次read方法和打印方法,打印的则是下一个
?System.out.println(char(x));//因为返回的是int型,所以要进行
类型转换。
?//一个文件里面显然不会只有一个字符,像上面所说读一个重写一次方法,会很麻烦,所以会想到
循环,如下:
?
??
?int x = 0;
?while((x=fr.read()) != -1)//文件读取到末尾如果没有的话会返回-1
?{
?
?System.out.println((char)x);
?}
?
?
?字符流---读取文件2 通过字符数组进行读取,下面是没有进行异常处理的
?FileReader fr = new FileReader("Demo.txt");//创建一个文件读取流对象和指定文件相关联
?//定义一个字符数组
?char[] ch = new char[1024]//一般数组大小会定为1024的整数倍
?int num = fr.read(ch);//将读取到的文字存放在数组中,则返回的是字符里面的个数
?System.out.println(new String(ch));//将数组变成字符串打印
?fr.close();//这一步没有刷新数据,读取文件不用刷新
?
?文件读取2的完整代码:
?
?
FileReder fr = null
try
{
fr = new FileReade();
char[] ch = new char[1024];
int num = 0;
while((num = fr.read(ch))!=-1)
{
//打印数组中的有效位,如果数组中只有三个字符,就没必要把1024个全打印出来
System.out.println(new String(buf,0,len));
}
}
catch (IOException e)
{
//异常处理
}
finally
{
try
{
if (fr !=null)
{
fw.close();
}
}
catch (IOException e)
{
//异常处理
}
}
??
?注意:
?
?定义文件路径时,可以用“/”或者“\\”。?
?
?在创建一个文件时,如果目录下有同名文 件将被覆盖。 ?
?
?在读取文件时,必须保证该文件已存在, 否则出异常。
?
练习:复制文件
?部分代码:
?
?
FileWriter fw = new FileWriter("copy.txt");//向目的文件中写入数据
FileReader fr = new FileReader("Demo.java");//读取文件中的数据写入流中
char[] buf = new char[1024];
int len = 0;
while((len=fr.read(buf))!=-1)
{
fw.write(buf,0,len);
}
??
---------------------------------------------------------------------------------------------
字符流的缓冲区: BufferedWriter ?BufferedReader
?
?字符流中的读写,如上面方法的话,都是一个一个的读或写,为了提高效率出现了缓冲区,就像
?用杯子接水滴,等接满了一杯再喝会比接一滴喝一滴舒服。
?像迅雷,BT这些软件它们在下载过程中都有自己的缓冲区,比如它们在服务器下载东西,每次下
?载一次性两兆再写到硬盘上从,比每次从服务器下载一点就往硬盘上写一点快吧。这边一顿狂读,
?这后到这边再一顿狂写,省的磁头来回的切换,这样效率就提升了很多。
?
?缓冲区的出现是为了提高流的操作效率而出现的。所以在创建缓冲区之前,必须要先有流对象。
?该缓冲区中提供了一个
跨平台的换行符。newLine();
?
?我们创建一个流对象之后,然后把它当参数传给缓冲区的构造函数,因为缓冲区已经和流相关联了,
?所以然后直接用缓冲区的方法就可以了。
?
?字符写入流缓冲区: BufferedWriter
?
?
??
??
?既然缓冲区是为了提高流的效率,那么它存在的前提是要有流。
?
?
??
?例如:
?
?FileWriter fw = new FileWriter("Demo.txt");
?
?//只需将需要被提高效率的流当参数传入给缓冲区的构造函数即可
?
?BufferedWriter bfw = new BufferedWriter(fw);
?
?//然后下面就可以用缓冲区的方法了(BufferedWriter也是Write的子类,所以也能用它的方法)
?
?bfw.write("abcde");
?
?bfw.flush();//只要用到缓冲区就要刷新
?
?bfw.close();//关闭缓冲区其是就是关闭了流对象,所以fw.close()就不用再写了
?
?缓冲区有一个新的方法:newLine()这个方法是跨平台的,在window上"\r\n"是换行而在linux上"\n"是换行
?只有用到缓冲区对象的时候才能用newLine()方法
?例如:
?
?for(intx =0;x<5;x++)
?
?{
?
?bfw.write("abcde");
?
?bfw.newLine();
?
?//为什么要写一次刷新一次? 因为在写的过程中可能会出现一些状况如停电,如果在写好多的情况下
?
?// 停电了没刷新那么文件里面一点也不会有
?
?bfw.flush();
?
?}
?
---------------------------------------------------------------------------------------------
?
字符读取流缓冲区: BufferedReader
?
?该缓冲区提供了一次读一行的方法:readLine()因为一行有很多字符。所以它返回的是字符串,读到流的
?末尾返回的是null,readLine方法返回的时候只返回回车符之前的数据内容。并不返回回车符。
?readLine()方法最终都是从硬盘上一个一个的读取所以最终使用的还是read方法一个一个的读的。
?
?//创建一个读取流对象和文件相关联。
?FileReader fr = new FileReader("Demo.txt");
?//为了提高效率。加入
缓冲技术。将字符读取流对象作为参数传递给缓冲对象的构造函数。
?BufferedReader bfr = new BufferedReader(fr);
?//下面使用BufferedReader的方法
?String s = null;
?while((s=bfr.readLine()) !=null)
?{
?
?System.out.print(s);
?}
?bfr.close();
?
?缓冲区特点:
?
?缓冲区的出现提高了对数据的读写效率。?
?
?缓冲区要
结合流才可以使用。?
?
?在流的基础上对流的功能进行了增强。
---------------------------------------------------------------------------------------------
?
装饰
设计模式:
?一个类的功能如果需要加强,可以在定义一个类来写加强的功能,然后将需要被加强的类
?当参数传入加强类,通过构造方法来接收被加强的类。如BufferedReader(FlieRader fr).
?
?模式例如:
?
class OldMethod //1.
{
public void fuction
{
//原有的功能
}
}
class SuperMethod //2.
{
private OldMethod om;
SuperMethod(OldMethod om)
{
this.om = om;
}
public void superFuction
{
//基于原有而加强的功能
//如果有用到原来的东西,直接调用:om.fuction()
}
}
??
---------------------------------------------------------------------------------------------
?
疑问:为什么类2不继承类1呢?
?
?例如下面类OldMethod有三个子类,比如在开发的过程中我们发现这三个子类里面的功能效率有点低我
?
?们想要重写个类来加强一下它们的功能,这时会想到用缓冲技术,那么如果要用继承的话,就会像下
?
?面这样,在后期OldMethod可能还会再增加好多子类,那么在这样写的话整个体系就会显得非常臃肿,所
?
?以要进行优化。
??
OldMethod
?|--OneMethod
?
?|--OneBufferMethod
?|--TwoMethod
?
?|--TwoBufferMethod
?|--
ThreeMethod
?
?|--ThreeBufferMethod
怎么优化呢?既然它们都需要缓冲,所用的技术也都一样,那么就没必要一个一个的写缓冲类来继承了,可以
专门定义一个缓冲类,OldMethod的哪个子类需要缓冲就把哪个当参数传进来。如下:
class MyBufferMethod
{
?MyBufferMethod(OneMethod one)
?{
?}
?MyBufferMethod(TwoMethod two)
?{
?}
?MyBufferMethod(ThreeMethod three)
?{
?}
?
??
?......//但是这样扩展型还是不太好
}
?
这时可以发现,无论是哪个类需要缓冲,
他们都是OldMethod的子类,这样就想到了多态,我们可以这样定义
一个类来继承.
class MyBufferMethod extends OldMethod//既然MyBufferMethod里的功能和只是那三个子类功能的加强,
{ ?
??
??
??
?//所以它也应该属于OldMethod参看下面的(看这里)
?MyBufferMethod(OldMethod old)//多态,技能传OneMethod也能传TwoMethod...
?{
?}
}
?
最后这个体系就变成了这样:
OldMethod
?|--OneMethod
?|--TwoMethod
?|--ThreeMethod
?|--MyBufferMethod
---------------------------------------------------------------------------------------------
我们可以看出
装饰模式比继承更灵活,降低了类与类之间的关系
装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了加强功能。所以装饰类
和被装饰类属于同一个体系就像BufferedReader与FileReader同是Reader的子类
---------------------------------------------------------------------------------------------
?
LineNumberReader: 也是一个装饰类,BufferedReader的子类,所以它也有readLine()方法,
?
??
??
??
??
??
??
??
??
??另外还有setLineNumber()和getLineNumber(),意为设置和获取行号。
?
使用方法如下(没有进行异常处理):
?FileReader fr = new FileReader("Demo.txt");
?
??
?LineNumberReader lnr = new LineNumberReader(fr);
?String s = null;
?lnr.setLineNumber(10);//可以设置行号的起始值
?while((x=lnr.readLine()) != null)
?{
?
?System.out.println(lnr.getLineNumber()+"::"+x);
?}
?
---------------------------------------------------------------------------------------------
?
?
字节流:基本操作与字符流差不多,别说它里面的write,read方法和字符流里的基本一样,它也能一个一个
的往文件里写数据,也能把数组中的数据一下写到文件中,既能一个一个的读,也能把数据读入到一个数组
中,唯一的区别是字符流写进的是char[],而字节流是往byte[]数组中读或写。另外字节流不仅操作字符,
还可以操作其他媒体文件
?
基类 InputStream读 OutputStream写
??
字节流--文件写入,与字符流一样也要处理异常。
?FileOutputStream fos = new FileOutputStream("Test.txt");//创建一个字节写入流,与文件相关联
?//里面要么写单个字节,要么写字节数组,写字符串的话要把它转成字节类型的
?fos.write("abcde".getBytes());//不需要刷新,因为是直接对字节最小单位操作,中间不需要任何转换
?fos.close();//关闭流
?
字节流--文件读取1,一个一个的打印
?FileInputStream fis = new FileInputStream("Test.txt");
?int ch = 0;//调用read()方法返回的是整数,流末尾返回的是-1;
?while((ch=fos.read())!=-1)
?{
?
?System.out.println(char(ch));//因为返回的是整数,所以要把它转成字符型打印
?}
?fos.close();?
字节流--文件读取2,将数据读进一个字节数组
?FileInputStream fis = new FileInputStream("Test.txt");
?byte[] buf = new Byte[1024];
?int len = 0;
?while((flen=os.read(buf))!=-1)
?{
?
?//把数组转换为字符串,按范围打印出来
?
?System.out.println(new String(buf,0,len));
?}
字节流--文件读取3,这一种是字节流特有的
?FileInputStream fis = new FileInputStream("Test.txt");
?//int num = fis.available();调用这个方法可以算出文件中的字符数
?byte[] buf = new Byte[fis.available()];//所已就可以定义一个刚刚好的数组了,也就不用循环了
?fos.read(buf);//读取文件中的字节存在数组buf中
?System.out.println(buf);//不用循环,直接把数组里面的全打印出来
?fis.close();
---------------------------------------------------------------------------------------------
?注意:
?
?字节流的读一个字节的read方法为什么返回
值类型不是byte,而是int。
?
?因为有可能会读到连续8个
二进制1的情况,8个二进制1对应的十进制是-1.
?
?那么就会数据还没有读完,就结束的情况。因为我们判断读取结束是通过结尾标记-1来确定的。
?
?所以,为了避免这种情况将读到的字节进行int类型的提升。
?
?并在保留原字节数据的情况前面了补了24个0,变成了int类型的数值。
?
?
?而在写入数据时,只写该int类型数据的最低8位。
?
---------------------------------------------------------------------------------------------
字节流缓冲区
?
?//例:通过字节流的缓冲区来完成 Mp3的复制:
?BufferedInputStream bufis = new BufferedInputStream(new FileInputStream("c:\\0.mp3"));
?BufferedOutputStream bufos = new BufferedOutputStream(new FileOutputStream("c:\\1.mp3"));
?
??
??int by=0;
?while((by=bufis.read())!=-1)
?{
?
?fos.write(by);//把从文件中读取的数据写进目的文件
?}
?
?fos.close();//有多少流就关多少流
?fis.close();
?
?
自定义字节流缓冲区:
?字节流缓存区的原理是,把一些数据先存进一个数组中,然后在一下取出来。自定义缓冲区的话,思路
?就是:要先定义一个数组,然后再把数组中的数据一个一个往外取。用到了数组,怎么往里存呢?我们
?知道read(arr)返回的是数组里元素的个数,
?那取的时候read方法
?是一个一个的往下读的,所以就用到了指针,那什么时候停呢?就是取到数组末尾的时候,就是count=0的时候,
?那怎么往数组里面存呢?
class MyBufferInputStream extends
{
private InputStream ips;//定义一个InputStream引用
private byte[] buf = new byte[1024*4];
private int pos = 0,count = 0;//定义一个指针和数据个数的变量
MyBufferInputStream(InputStream ips)//把需要缓存的文件当参数传给缓冲器的构造函数
{
this ips = ips;
}
//要对外提供一个read方法,就像BufferInputStream,一次读一个字节(从缓冲区即字符数组中读取)
public int myRead()throws IOException//进行异常处理
{
//通过in对象读取硬盘上数据,并存储buf中。
if(count==0)//5.所以当数组中元素为0的时候,是先存再取
{
count = ips.read(buf);//1.把数据读取到数组中,返回的是数组里元素的个数
if(count<0)//
return -1;
pos = 0;//
byte b = buf[pos];//2.往数组里存满了是不是就该把数组里的数据取出来放缓冲区里啦
count--;//3.把数组里的数据往缓冲区里装的时候是不是装一个,数组里就少一个啊?
pos++;//4.一个一个的把数组里的取出来,是不是就是指针越来越往后移啊
return b&255;
}
else if(count>0)//6.而当数组里有数据的时候直接取
{
byte b = buf[pos];
count--;
pos++;
return b&0xff;
}
return -1;
}
}
??
读取键盘录入:
?System.out:对应的是标准输出设备,控制台。
?System.in:对应的标准输入设备:键盘。in 是标准输入流对应的是InputStream,所以能用read()等方法
?例如:
?InputStream in = System.in;//System调用in方法之后返回的是一个字节读取流
?int by = in.read();//读取一个字节字符流字节流读取一个数据返回的都是int型
?System.out.println(by);//控制台将会等待输入运行代码
?
?部分代码:
import java.io.*;
class IODemo
{
public static void main(String[] args) throws IOException
{
InputStream ins = System.in;
StringBuilder sb = new StringBuilder();
while(true)
{
int ch = ins.read();
if(ch=='\r')
continue;
if(ch=='\n')
{
String s = sb.toString();
//sb.delete(0,sb.length());
if("over".equals(s))
break;
System.out.println(s.toUpperCase());
sb.delete(0,sb.length());
}
else
sb.append((char)ch);
}
}
}
??然后发现以上代码与我们自定义 BufferedReader时写的ReaderLine代码差不多,这时就考虑能不能
?直接拿过来用呢?但是 BufferedReader是字符流,而InputStream是字节流,它是字节流要用字符流
?的东西,怎么办?是不是要看看有没有能把字节流转为字符流的东西啊?通过查看java API发现,还
?真有个转换流,下面请看java io(2)。
----------------------?android开发、java培训、期待与您交流! ----------------------