IO流字符流和字节流的概述

来源:互联网 发布:ubuntu安装终端 编辑:程序博客网 时间:2024/05/29 15:42

------- android培训、java培训、期待与您交流! ----------

1 IO流
IO流用来处理设备之间的数据传输
java数据的操作是通过流的方式
流按操作数据分为两种:字节流与字符流
流按照流向分为:输入流,输出流

1.1)字节流的抽象基类:InputStream和OutStream
1.2)字符流的抽象基类:Reader和Writer

/*写一个操作字符流的简单例子*/java.io.*;class FileWriterDemo{public static void main(String args)throws IOException{    //创建一个FileWriter对象.该对象一被初始化就必须要明确被操作的文件    //而且该文件会被创建到指定目录下,如果该目录下已有同名文件,里面的数据将被覆盖    //其实该步就是在明确要存放的目的地FileWriter fw=new FileWriter("demo.text");//该方法可能有异常,要么抛,要么try,如果抛给了调用者,同时异常又刚刚好出现的话,就会抛给下一个调用者,如果没有碰到异常,运行就正常      ("h:\\demo.text")这样的异常//调用write方法,将字符串写入到流中fw.write("abcd");//刷新流对象中的缓冲中的数据//将数据刷到目的地//fw.flush();   //关闭流资源,但是关闭之前会刷新一次内部的缓冲中的数据   //将数据刷到目的地   //和flush区别:flush刷新后,流可以继续使用,close刷新后,会将流关闭fw.close();}}

//例子2:上面简单操作字符流的优化代码import java.io.*;class FileWriterDemo2{public static void main(String[] args){FileWriter fw=null;//FileWriter引用要定义在外面,如果在下面的try代码块里面,其他代码块会不认识这个变量,也可以写成FileWriter fw;但是呢我们说变量定义最好初始化一下try{//fw=new FileWriter("d:\\demo2.txt",true);构造函数的时候传入一个true参数,代表不覆盖已有的文件,并在已有的文件的末尾处进行数据续写,传入false表示如果文件名相同则覆盖已有数据//注意:外面定义了FileWriter fw,里面就不要再定义了,直接拿来用就完事了,不然会出现在main函数里已经定义的错误fw=new FileWriter("d:\\demo2.txt");fw.write("cdkfe");fw.flush();}catch(Exception e){e.toString();}finally{try{if(fw!=null)//这里要判断fw是否为空,因为如果fw没有创建成功的话,null.close()会抛出空指针异常fw.close();}catch(Exception e){e.toString();}}}}

读取流对象的建立和使用:读取流对象的第一种方式read()方法

//例子1:读取流对象,一个一个的读import java.io.*;class FileReaderDemo{public static void main(String[] args)throws IOException{//创建一个文件流对象,和指定文件名称的文件相关联//要保证该文件是已经存在的,如果不存在,会发生异常FileNotFoundExceptionFileReader fr=new FileReader("demo5.txt");int a;调用读取流对象的read()方法while((a=fr.read())!=-1){System.out.println((char)a);}//这个close()方法仅仅是关闭流对象,没有刷新流对象fr.close();}}读取流的第二种方式:read(char[] buf)import java.io.*;class FileReaderDemo2{public static void main(String[] args)throws IOException{FileReader fr=new FileReader("demo3.txt");//定义一个字符数组,用于存储到字符,数组长度一般为1024//该read(char[])返回的是读到字符个数//读取过程:调用read()方法,先读文件的第一个字符,读到之后把字符储存在字符数组,然后再读第二个字符,读到再存储在数组中,当读到末尾的时候就返回-1//注意如果数组不够大,而read()方法再继续读的话,数组的指针会重新回到开始位置,把读到的字符存储到数组中,会把数组原来的数覆盖掉.  但读到文件的末尾时,就不再读了,返回-1char[] buf=new char[1024];int num=0;while((num=fr.read(buf))!=-1){//这里用到了String(char[],int start,int count)方法 返回值:String//start是数组想要打印位置起始的下标,count是个数System.out.println(new String(buf,0,num));}fr.close();}//另外:String(char[] value)返回值是String 返回的是字符数组里面的内容}

1.3)将C盘一个文本文件复制到D盘
复制原理:
其实就是将C盘下的文件数据储存到D盘的一个文件中
步骤:
1在D盘创建一个文件.用于存储C盘文件中的数据
2定义读取流和C盘文件关联
3通过不断的读写完成数据的存储
4关闭资源
/*将C盘一个文本文件复制到D盘*/import java.io.*;class ReadWrite{public static void main(String[] args){FileReader fr=null;FileWriter fw=null;try{fr=new FileReader("c:\\httpdownload.txt");fw=new FileWriter("d:\\httpdownload.txt");char[] chs=new char[1024];int len=0;while((len=fr.read(chs))!=-1){fw.write(chs,0,len);//len是read(chs)方法一次读取之后,返回的读取个数}}catch(IOException e){throw new RuntimeException("读写失败");}finally{if(fr!=null)try{fr.close();}catch(IOException e){e.toString();}if(fw!=null)try{fw.close();}catch(IOException e){e.toString();}}

2 缓冲区:BufferedWriter和BufferedReader
缓冲区的出现是为了提高流的操作效率而出现的
所以在创建缓冲区之前,必须要先有流对象

/*字符写入流缓冲区例子:*/import java.io.*;class BufferedDemo{public static void main(String[] args)throws IOException{//创建一个字符写入流对象FileWriter fw=new FileWriter("deom.txt");//为了提高字符写入流效率,加入了缓冲技术//只要将需要被提高效率的流对象作为参数传递给缓冲区的构造函数即可BufferedWriter bw=new BufferedWriter(fw);for(int x=0;x<5;x++){bw.write("abce");//windows里面的换行写法是:\r\n  而Linux换行写法是:\n  newLine()方法把这两个写法都封装在里面了,按照jdk的时候要判断是什么系统,newLine根据系统写入就行了    其实原理调用就是:bw.write("\\r\\n");bw.newLine();//bw.write("\r\n");bw.flush();//记住,只要用到缓冲区,就要记得刷新,这里每写一条就刷新一下,是因为调用bw.wrte("")方法写入的字符,暂时是存储在缓冲区的,怕万一停电,缓冲区就没了,所以最好写一条就刷新一下}/其实关闭缓冲区,就是在关闭缓冲区中的流对象bw.close();//关闭该流,但要先刷新它}}

2.1)字符读取流缓冲区BufferedReader:

该缓冲区提供了一个一次读一行的方法readLine(),方便对于文本数据的获取
/*这是一个缓冲区读取流的代码*/import java.io.*;class BufferedReaderDemo{public static void main(String[] args)throws IOException{FileReader fr=new FileReader("demo.txt");//创建流对象BufferedReader br=new BufferedReader(fr);//创建缓冲区String s=null;while((s=br.readLine())!=null){System.out.println(s);}br.close();}}

2.2)通过缓冲区复制一个.java文件

/*例子:通过缓冲区复制一个.java文件*/import java.io.*;class CopyTextDemo{public static void main(String[] args){BufferedReader bufr=null;BufferedWriter bufw=null;try{bufr=new BufferedReader(new FileReader("第十九天笔记.txt"));bufw=new BufferedWriter(new FileWriter("复制过来的笔记.txt"));String line=null;while((line=bufr.readLine())!=null){bufw.write(line);bufw.newLine();bufw.flush();}}catch(IOException e){throw new RuntimeException("读写失败");}finally{if(bufr!=null)try{bufr.close();}catch(IOException e){throw new RuntimeException("关闭失败");}if(bufw!=null)try{bufw.close();}catch(IOException e){throw new RuntimeException("关闭失败");}}}}BufferedReader.readLine()一次读一行,返回值类型是String,当读到结尾的时候就返回null注意:readLine()方法是不读取换行符的,所以我们写代码时,如果是多行数据,要在readLine()方法后面,调用newLine()方法

2.3)readLine()方法和数组读取流方法的区别:

readLine()返回值类型是Strng类型,而数组读取流返回值类型是int
readLine()读到行结束符的时候,就返回null,而且是不读取行标识符的,而数组读取的方法读到数据末尾是返回-1,而且是要读行终止符的



2.4)自定义一个缓冲区,自己做一个readLine()方法和close()方法,复制文件

/*例子:自定义一个缓冲区,自己做一个readLine()方法和close()方法,复制文件*/import java.io.*;class myBufferedReader extends Reader{/*//覆盖父类的抽象方法public void close()throws IOException{r.close();//直接调用子类的方法}public int int(char[] chs,int offer,int count)throws IOException{r.int(chs,offer,count);//覆盖的时候直接调用子类的方法}*/FileReader r;myBufferedReader(Reader r){this.r=r; //对象的r指向新建的FilewReader对象}public String myReadLine()throws IOException{//程序每次调用该方法,都会创建一个容器sbStringBuilder sb=new StringBuilder();int num=0;while((num=r.read())!=-1) //read方法是当没有数据时就返回-1,或者说读到数据的末尾就返回-1{if((char)num=='\r') //判断如果读到字符'\r'就继续读下一个字符continue;if((char)num=='\n') //判断如果读到'\n'结束循环,先把容器sb中的字符变成字符串,再返回   return sb.toString();//因为elsesb.append((char)num);  //先把num转为char类型,再把num存到容器中}if(sb.length()!=0)  //这句代码是为了防止,文件最后一句没有/r/n,这样容器sb中的内容就返回不了return sb.toString();//程序调用该方法时,当读到末尾,没有字符可读,while循环不执行,返回nullreturn null;}public void myClose()throws IOException{r.close();}}class CopyTextDemo{public static void main(String[] args){myBufferedReader mybufr=null;BufferedWriter bufw=null;try{mybufr=new myBufferedReader(new FileReader("第十九天笔记.txt"));bufw=new BufferedWriter(new FileWriter("复制过来的笔记.txt"));String line=null;while((line=mybufr.myReadLine())!=null){bufw.write(line);bufw.newLine();bufw.flush();}}catch(IOException e){throw new RuntimeException("读写失败");}finally{if(mybufr!=null)try{mybufr.myClose();}catch(IOException e){throw new RuntimeException("关闭失败");}if(bufw!=null)try{bufw.close();}catch(IOException e){throw new RuntimeException("关闭失败");}}}}

3 装饰设计模式:
当想要对已有的对象进行功能增强时,
可以定义类,将已有对象传入,基于已有的功能,并提供加强功能.
那么自定义的该类称为装饰类


3.1)装饰类通常会通过构造方法接受被装饰的对象
并基于被装饰的对象的功能,提供更强的功能

3.2)装饰设计模式和继承的区别:
装饰设计模式比继承更灵活,避免了继承体系臃肿
而且降低了类与类之间的关系

装饰类因为是增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能,所以装饰类和被装饰类通常都是属于一个体系

4LineNumberReader是BufferedReader的子类
LineNumberReader lnr=new LineNumberReader(new FileReader("1.txt"));
多了两个方法:
lnr.getLineNumber();获取行号
lnr.getLineNumber();设置行号


5 字节流

需求:想要操作图片数据,这时就要用到字节流

FileInputStream fi=new FileInputStream("1.txt");
byte by=new byte[fi.available()];
读取方法跟字符流基本上是一样的,不过有一个特有方法:available();
fi.read(Byte[fi.available()]); //这样就不用定义循环了,因为fi.read(Byte[])
数组长度是多少,就读多少数据
fi.available()  //创建一个跟读取文件刚刚大小的数组,不过不建议使用,因为万一数据1g,创建的数组比内存还大呢!


fi.write("abcde".getBytes)  //getBytes()把字符串转为字节数组
字符串转为字符数组:toCharArray();

注意:不要用字符流来读取图片,因为字符流是需要查编码表的,如果没有对应的码表,就会走未知区域的码表,返回乱码,导致图片打不开

5.1)自己做一个字节流缓冲区read方法

/*这是自己做的字节流缓冲区read方法*/import java.io.*;class MyBufferedInputStream //extends InputStream{private InputStream fis;byte[] b=new byte[1024]; //定义一个byte类型的数组int pos=0;int count=0;MyBufferedInputStream(InputStream fis)//构造函数初始化{this.fis=fis;}public int myRead()throws IOException//创建一个缓冲区读字符的方法{if(count==0){  //利用FileInputStream的read方法,从硬盘读一批数据存到数组里count=fis.read(b); if(count==-1)return -1;pos=0; //从硬盘读一批数据存到数组,让pos归零byte b1=b[pos];count--;return b1&255;}else if(count>0){byte b1=b[pos];pos++;count--;/*注意:这里写return b[pos]&255;会报角标越界异常一定要先用b1记住b[pos]的值,因为数组长度为1024,最大的角标只有1023,当count减到0时,pos就等于1024了,所以下面的return语句不要写成:return b[post]&255   这样写是错误的*/return b1&255; //&上255得出最后四位}return -1;}public void myClose()throws IOException //创造关闭资源的方法{fis.close();}}class MyBufferedDemo{public static void main(String[] args)throws IOException{//定义一个读取字节的输入流对象文件FileInputStream fis=new FileInputStream("1.jpg");//定义一个输出字节的流对象文件FileOutputStream fos=new FileOutputStream("3.jpg");//创建一个自定义的缓冲区对象MyBufferedInputStream mbf=new MyBufferedInputStream(fis);int num=0;while((num=mbf.myRead())!=-1){fos.write(num);}mbf.myClose();fos.close();}}

6) 字符流和字节流的总结
字符流操作文件用:FileReader和FileWriter
读写字符有两种方法:
6.1)读一个写一个:
int a=fr.read();//返回值是int类型,a是字符的ASCII值,一个一个的往下读,当没有数据了就返回-1
对应的写方法:void fr.writ(int a);//接受一个int类型的值,通过ASCII编码表自动转为字符

6.2) 读一个先存到数组,再把数组写到里面去
int b=fr.read(char[]); //返回值类型是int,返回的是所读字符的个数,如果数组长度为100,那么先从源文件中读取100个字符存到数组里面,如果读到最后源文件只剩下20个字符了,把这20个字符存到数组里去

对应的数组写方法:void fw.write(char[],0,b);//把每次数组所读的数据都存在目的文件去
int len=0;
while((len=fr.read(chs))!=-1)
//往数组里取数据,当数组数据取完就把所取字符个数返回,调用write方法,把数组里的数据写进去
{
fw.write(chs,0,len);
}

读写字节有两种方法:
字节流:FileInputStream和FileOutStream
读数据:
6.3)一次读一个和一次写一个:
int a=fis.read();//返回值类型是int,返回的是读到字节的ASCII值,如果直接打印的话,是一个数字,如果本来是字符,可以把他变成字符型(char),可以知道是什么类型

对应的写:void fos.write(a);//把所读数据的ASCII值存进去,内部自动转为字节型


6.4)读到的数据先存到数组:
int len=0;
int len=fis.read(char[]);//返回值类型是int,返回的是所读字符的个数
对应的写:void fos.write(chs,0,len); 一次把数组里的东西写到目的地

7 读取键盘录入.字节通向字符的桥梁InputStreamReader(InputStream ins)
改变键盘录入的源对象:System.setIn(new FileInputStream("1.txt"));

改变控制台目的对象方法:System.setOut(new PrintStream("2.txt"));

7.1)记住,转换流什么时候使用.
通常涉及到字符编码转换时,需要用到转换流,因为转换流可以指定编码表
FileInputStream,FileOutputSteam和FileReader,FileWriter都使用系统默认编码表:GBK
OutputStreamWriter osw=new OutputWriter(new FileOutputStream("d.txt","utf-8"));

/*键盘录入的例子当录入一行数据后,就将该数据打印当键盘输入over,那么就停止录入*/import java.io.*;class ReadIn{public static void main(String[] args)throws IOException{//键盘录入最常见写法BufferedReader bufr=new BufferedReader(new InputStreamReader(System.in));OutputStream out=System.out;//控制台输出OutputStreamWriter ouw=new OutputStreamWriter(out);BufferedWriter bffw=new BufferedWriter(ouw);String line=null;while((line=bufr.readLine())!=null){if(line.equals("over"))break;bffw.write(line.toUpperCase());bffw.newLine();//换行bffw.flush(); //刷新流对象}bufr.close();bffw.close();/*方法1//获取键盘录入对象InputStream in=System.in;//讲字节流转换为字符流,使用转换流InputStreamReader inr=new InputStreamReader(in);//为了提高效率,使用了缓冲区技术高效BufferedReader bufr=new BufferedReader(inr);String line=null;while((line=bufr.readLine())!=null){if(line.equals("over"))break;System.out.println(line.toUpperCase());}方法二InputStream in=System.in;int a=0;StringBuffer sb=new StringBuffer();while(true){a=in.read();if(a=='\r')continue;else if(a=='\n'){String s=sb.toString();if("over".equals(s))break;System.out.println(s.toUpperCase());sb.delete(0,sb.length());}elsesb.append((char)a);}*/}}

8流操作的基本规律:
最痛苦的就是流对象太多,不知道该用那个
8.1)通过两个明确来完成.
 1.明确源和目的
        源:输入流.  InputStream   Reader
目的:输出流   OutputStream Reader
  2.操作的数据是否是纯文本
是:字符流.
不是:字符流
3.当体系明确后,再明确要使用那个具体的对象
通过设备来进行区分
     源设备:内存,硬盘,键盘
    目的设备:内存,硬盘,控制台
4.是否要提高效率

例子:将一个图片文件中数据存储到另一个文件中,复制文件,按照上面步骤:  1明确源:InputStream  Reader  2是否是纯文本:不是   使用InputStream  3明确具体对象:设备是内存中的一个图片文件,那么字节流中操作文件的对象是  FileInputStream  4是否要提高效率:BufferedInputStream 对象  1明确目的:OutputStream Writer  2是否是纯文本:不是   使用OutputStream  3明确设备  硬盘里的一个图片文件    那么字节流里操作文件的对象是FileOutputStream  4是否要提高效率:是   使用BufferedoutputStream


/*例子1:把系统信息打印在文本文件*/import java.util.*;import java.io.*;class SystemInfo{public static void main(String[] args)throws IOException{Properties prop=System.getProperties();prop.list(new PrintStream("systeminfo.txt"));}}

9 把异常打印在指定文件上

/*把异常打印在指定文件上方法:e.printStackTace(PrintStream p) */import java.util.*;import java.text.*;import java.io.*;class IndexException{public static void main(String[] args)throws IOException{try{int[] a=new int[2];System.out.println(a[3]);}catch(Exception e){Date d=new Date();SimpleDateFormat sdf=new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");//按照自己需求指定日期格式String s=sdf.format(d);//把日期格式化PrintStream ps=new PrintStream("Excption.txt");ps.write(s.getBytes());//把日期写到目的文件e.printStackTrace(ps);//把异常信息,写入到目的文件}}}




 ------- android培训、java培训、期待与您交流! ----------