------- android培训、java培训、期待与您交流! ----------
IO流--字符流。
IO流用来处理设备之间的数据。java对数据的操作是通过流的方式进行的java用于操作的流对象都在IO包中
流按照操作数据分为字节流和字符流流按照流向分为输入流和输出流。
为什么有字符流的存8在?
大写A 65 小写 a 97 ASCII码中的编码形式汉字的编码 是GB2312收录几千个中文文字和标点,扩容之后叫做
GBK,后面还有扩容。国际标准化组织的编码表 Unicode码表,16个位表示一个字符,优化之后叫做UTF-8.
这样出现一个问题,一个数字对应多个不同码表中的内容。如果写中文存入电脑中时查的是其他码表会产生乱码。
java问了解决这个问题就产生了字符流,就是说字符流可以指定码表进行处理。文字用字符流,图片等这样的数据
用字节流。通用字节流,因为字符流基于字节流。字节流的两个抽象基类
InputStream,OutputStream.
字符流的抽象基类
Reader Writer
他们的子类后缀都会继承类名。
Writer
体系中主要方法就是写。数据最常见的表现形式就是文件。那么先以操作文件来演示这个类的功能
FileWriter//专门用于操作文件的Writer子类对象。
此类没有空参数的构造方法,这个流对象一初始化就应该有可以写入数据的文件存在。
FileWriter fw=new FileWriter ("k:\\demo.txt")//路径写错,写成不存在的路径。所以会抛异常,必须抛异常,或者捕捉,自己处理。
以上代码创建了一个流类对象,该文件会被创建到指定的目下,如果当前目录下有同名文件,将会覆盖该文件。
fw.write("abcd");//写入了字符串数据,写入了流当中,流在内存中,也就是写入了内存。
fw.flush();//将缓冲区中临时存放的数据清空,并存入与该流关联的文件中。
此时这个demo.txt文件中将会存入abcd.
fw.close();//关闭流资源,关闭之前会刷新一次流内部缓冲中的数据。同样会刷入关联文件中。flush()刷新后,流可以继续使用,close刷新后流不能再使用。实际上,java是在调用系统内部的方法完成数据的书写。所以使用完就需要关闭这些资源。
这些方法的调用会发生IO异常,所以需要进行相关处理。以下是常用的处理方式。
FileWriter fw=null
try{
fw=new FileWriter ("k:\\demo.txt");//这么写才成功。
//FileWriter fw=new FileWriter ("k:\\demo.txt");//此变量在try中建立无法被捕获,所以应该先在外面建立引用。
fw.write("abcd");
}catch(IOException e){
system.out.println("catch:"+e.toString())
}finally{
//fw.close();//fw创建对象可能不会成功,所以这里调用方法会抛异常,也要处理。
try{
if(fw!=null)
fw.close();
}catch(IOexception e){
system.out.printlnt(e.toString();)
}
}
记住:有些功能如果是给别人去用的,可能会发生异常,那么声明异常就行,让调用者去处理。
以下代码模拟readLine方法的读一行
StringBuilder sb=new StringBuilder();
int ch=0;//定义一个接收read方法返回值的int类型,方便用于判断是否读到末尾。此方法应该定义在一个类中,相当于自己写了一个人BufferedReader类,并且该类中应该有一个Reader类成员用于调用read方法,该类还需要继承Reader类。因为作为缓冲类,它增强的只是某一个功能,而其它功能的实现还得去继承,比如说close方法。
while((ch=r.read())!=-1){//是基于read方法
if(ch=='\r')
continue;//因为后面可能会有\n,所以继续读。
if(ch=='\n')
return sb.toString();//代表读到行的结尾处,返回一行数据。
else
sb.toString();//否则,继续添加。
}
if(sb.toString!=null)
return sb.toString();//如果读到最后一行,是没有回车符的,这时候判断一下sb中有没有数据,有就返回。
return null;
装饰设计模式,当想要对已有的对象进行功能增强时,可以定义一个类,将已有对象传入,基于已有对象的功能并提供加强功能,那么自定义的该类就称为装饰类。比如说,这个BufferedReader增强功能readLine就是基于read方法读一行。这个BufferedReader就可以看作是一个装饰类。
装饰设计模式要比继承更加灵活,避免了继承体系的臃肿。而且降低了类与类之间的关系。
装饰类因为增强了已有对象,具备功能和已有的是相同的,只不过是提供了更强的功能,所以装饰类和被装饰类通常都属于一个体系中的。是组合结构的一种体现。组合结构就是B类类作为A的一个成员。
一种非常简便的覆盖形式是,直接返回父类方法的返回值。
LineNumberReader同样是装饰类,可以获得行号的装饰类。他是继承自BufferedReader类。
读取不用刷新,写入才会刷新。
readLine 返回一行String数据,用于文本读取。
writer类的write()可以传入可以传入单个字符,char数组,字符串。要刷新
fileWriter的构造函数关联文件要注意,文件不能写错,可能会抛异常。不存在则会创建,存在则会覆盖,没有绝对路径就会创建在当前目录下。
bufferedWriter类中有一个newLine用于写入一个行分隔符,也可以写一个字符串。
以下代码展示字符流文本复制过程。
public class IOdemo09 {
public static void main(String[] args) throws IOException {
FileWriter fw=new FileWriter("demo_copy.txt");
FileReader fr=new FileReader("demo.txt");
char[] buf=new char[4];
int num=0;//num为该读取方法返回的读取数
while((num=fr.read(buf))!=-1)
fw.write(buf,0,num);//写入一部分,防止把没有数据的内容写入
//不用刷新,没有用到缓冲区,记住了,之所以不用刷新是因为最后关了一次close方法会
//刷新一次。数组的大小为4时,也能将数据都写入文件中,而且只是最后一次刷新,
//这说明,该流内部有一个缓冲区用于临时存储数据。
fr.close();
fw.close();
//try catch代码块自行处理时有所不同。
}
}
IO流--字节流
字节流:
InputStream OutputStream
OutputStream
不同之处就是使用的数组不同,字节流使用字节数组.
void write(byte[] b)
void write(byte[] b, int off, int len)
方法名还是write
FileOutputStream fos=new FileOutputStram("fos.txt")//创建一个OutputStream的子类字节流。
fos.write("abcd".getBytes();)//字符流同样走的是字节,字符流读多个字节需要缓冲一下(因为需要查表),而字节流不需要查表,不需要刷新。但是流资源需要关闭。字符串转换成字节数组。
fos.close();
InputStream中有一个available用于拿到文件中字节总数,因此可以定义一个刚刚好的byte数组用于依次取完文件中的数据。而不用进行循环操作。但是如果文件过大,则不能这么做。会发生内存溢出。因为是读到内存的流当中,也就是内存中。
同样也有一个BufferedOutputStream类以及BufferedInputStream类作为两个流的缓冲,与字符流不同的是没有读一行的方法,读一个字节,或者读一组字节。
注意:缓冲区是这样的,如果定义了一个读取流类对象关联了一个文件,接着定义这个流的缓冲区的时候,是先把这个文件中的数据弄到缓冲区中,然后重缓冲区当中来读,写的方式也是一样的,这里要理解清楚,不然并不清楚这个缓冲区的意义何在。
字节流需要注意的一点就是,字节流可能会出现读到-1的情况,所以自己定义的缓冲区间需要注意一个问题就是想要保证byte数据在提升到int类型后还能不变,需要&0xff,这将会在前面填充0,之后强转为byte则可以正确拿到数据。(byte数据占8个字节)
字节流中的write方法实际上是做了一个强转的动作,将接收的int强制转换成了byte。
以下代码展示字节流复制文本过程
public class IOdemo14 {
public static void main(String[] args) throws IOException {
FileInputStream fis=new FileInputStream("abc.pdj");
FileOutputStream fos=new FileOutputStream("abc_copy.pdj");
byte[] buf=new byte[1024];
int num=0;
while((num=fis.read(buf))!=-1){
fos.write(buf,0,num);
}
fis.close();
fos.close();
}
}
自定义字符输入流缓冲:
class MyBBufferedReader{
private FileReader fr;
MyBBufferedReader(FileReader fr){
this.fr=fr;
}
public String myReadLine(FileReader fr) throws IOException{
StringBuffer sbf=new StringBuffer();
int num=0;
while((num=fr.read())!=-1){//read方法返回一个int类型,
if((char)num=='\r')//不用强制转换,记住。
continue;
else if((char)num=='\n')
return sbf.toString();//可能会出现读到最后一行
//数据没有换行符的情况这时就需要判断一次sbf中是否有内容
//有的话将它返回。
else
sbf.append((char)num);//需要强制转换,返回的是该字符
}
if(sbf.length()!=0)//这段代码的作用是如果读到最后一行却没有
//换行符时,需要判断一下sbf这个缓冲中有没有内容,,有的话就将它返回。
return sbf.toString();
return null;
}
public void myClose() throws IOException{
fr.close();
}
}
自定义字节缓冲读取流:
class MyInputStream{
private InputStream in;
private int count=0,pos=0;
//这个指针必须定义为函数成员,因为
//该方法会被循环调用多次,不能每调用一次就创建一次
private byte[] buf=new byte[1024];
MyInputStream(InputStream in){
this.in=in;
}
public int Myread() throws IOException{
if(count==0){
count=in.read(buf);
if(count<0)
return -1;
pos=0;
byte b=buf[pos];
count--;
pos++;
return b&255;//有可能会发生读到11111111这个字节的情况,问题就在于我们返回的是一个
//int类型的数据,那么,该数据可能会被提升为-1,使用自己定义的缓冲区的myRead方法时,
//就可能将读到的数据作为-1来返回,这与定义的-1标记有冲突,所以需要将所有的读到数据
//都不能以-1的int形式返回,为了防止这样的情况发生,将读到的byte字节进行int提升时,
//将其前面的扩充位数都定义为0,而这么做就不会出现都是1的情况(注意-1的int表示为全部
//位数都为1),就这么轻松愉快的解决了问题。
//所以需要将b提升为int类型,而且前面补将b与上一个255
//注意该方法返回的是一个int类型,如果传入给BufferedOutputStream
//的write(int a)作为参数时,该方法只会写入该int数据的最低八位,也就
//是一个字节,而不是四个。
}
else if(count>0){
count--;
byte b=buf[pos];
pos++;
return b&255;
}
return -1;//必须加一个return语句。
}
}