`
小驴变黑马
  • 浏览: 12018 次
文章分类
社区版块
存档分类

黑马学习日记_IO篇(第一部分)

阅读更多
---------------------- 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在调用windos中那个写的动作?这个就是我们所说的流
 资源。其实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培训、期待与您交流! ----------------------
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics