黑马程序员——IO流(字节流)



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


IOInput Output)流

IO流用来处理设备之间的数据传输

Java对数据的操作是通过流的方式

Java用于操作流的对象都在IO包中

IO流常用基类

 字节流的抽象基类

     InputStream    OutputStream

 字符流的抽象基类

     Reader   Writer

注:有这四个类派生出来的子类名称

   都是以其父类作为子类名称的后缀。

   如:InputStream的子类FileInputStream

   如:Reader的子类FileReader


                     





IO流分类


字节流:

为了方便理解,可以把InputStreamOutputStream比作两根“水管”。




InputStream的常用方法

intread()从输入流读取一个8位的字节,把它转换为0255之间的整数,并返回这一整数

intread(byte[] b)从输入流读取若干字节,把他们保存到参数b指定的字节数组中,返回的整数表示读到的字节数

intread(byte[] b,int off,int len)从输入流读取若干字节,把他们保存到参数b指定的字节数组中,off指定字节数组开始保存数组的起始下标,len表示读取的字节数。

voidclose()关闭此输入流并释放与该流相关的所有系统资源

OutputStream的常用方法

voidwrite(int b)向输入流写入一个字节

voidwrite(byte[] b)把参数b指定的字节数组的所有字节写到输出流

voidwrite(byte[] b,int off,int len)将指定byte数组中偏移量off开始的len个字节写入输出流

voidflush()刷新此输出流并强制写出所有缓冲的输出流

voidclose()关闭此输出流并释放与此流相关的所有系统资源




读文件

public static void readFile_1()throws IOException

{

   FileInputStreamfis = new FileInputStream("fos.txt");

   int len = 0;

   while((len=fis.read())!=-1)

   {

       System.out.print((char)len);

   }

   fis.close();

}

//此方法才是最优化的方法

public static void readFile_2()throws IOException

{

   FileInputStreamfis = new FileInputStream("fos.txt");

   byte[] b =new byte[1024];

   int len = 0;

   while((len=fis.read(b))!=-1)

   {

       System.out.println(newString(b,0,len));

   }

   fis.close();

}


写文件

public static void writeFile()throws IOException

{

   FileOutputStreamfos = new FileOutputStream("fos.txt");

   //把字符串变成字节数组写入

   fos.write("abcd".getBytes());

   fos.close();

}

字节流缓冲区

BufferedInputStreamBufferedoutputStream

//通过字节流的缓冲区完成复制

public static void copy ()throws IOException

{

   BufferedOutputStreambos = new BufferedOutputStream(newFileOutputStream("cmj_copy1.mp3"));

   BufferedInputStreambis = new BufferedInputStream(new FileInputStream("cmj.mp3"));

   int by = 0;

   while((by=bis.read())!=-1)

   {

       bos.write(by);

   }

   bos.close();

   bis.close();

}

根据字节流输入流缓冲区原理,自定义的字节输入流缓冲区。

import java.io.*;

class MyBufferedInputStream

{

   privateInputStream in;

   privatebyte[] buf = new byte[1024];

   private intpos = 0,count = 0;

   MyBufferedInputStream(InputStreamin)

   {

       this.in = in;

   }

   //一次读一个字节,从缓冲区(字节数字)中获取

   public int myRead()throws IOException

   {

       //通过in对象读取银盘上数据,并存储在缓冲区buf中。

       if(count==0)

       {

           count= in.read(buf);

           if(count<0)

               return-1;

           pos =0;

           byteb = buf[pos];

           count--;

           pos++;

           returnb&255;

       }

       elseif(count>0)

       {

           byteb = buf[pos];

           count--;

           pos++;

           returnb&0xff;

       }

       return-1;

   }

   public voidmyClose()throws IOException

   {

       in.close();

   }

}



复制MP3文件时,出现了错误,文件的字节没有复制过去,为什么会出现这种情况呢?

MP3数据都是二进制数据,都是由0和1组成的,在数据的组成中会出现一个字节都是1的情况

11111111 就是8个1,而8个1刚好是十进制的-1,当取到一个byte类型的-1时,

我们所定义的返回类型是int类型,自动进行了类型提升,int是4个字节,

11111111 提升为int类型时 是 11111111 11111111 1111111111111111 前面自动补1

byte -1 -----> int -1

11111111----->1111111111111111 11111111 11111111

当循环判断时 -1 代表文件里没有数据,所以循环停止,导致数据没有复制成功

为什么用4个字节的int类型来做返回值呢?

就是为了避免在取一个字节时出现-1的情况,二进制中1开头表示负数,0开头表示正数

所以在类型转换(byte-->int)时,在前面补0站位

00000000 00000000 0000000011111111 8个1在补0之后等于255

所以把取到的字节 & 255 就可以避免复制时出现8个1的情况

 11111111 11111111 11111111 11111111

&00000000 0000000000000000 11111111

-------------------------------------

 00000000 00000000 00000000 11111111


在取的时候数据由1个8位变成4个8位,那这样复制的时候,原数据不就变大了吗?

不会,read()方法在读入的时候进行类型提升,在补0,是为了避免-1的情况发生

为写出方法write(),则自动做了强转,把最低的8位写出去,剩下的都舍弃

所以取到的数据还是原数据。

装饰设计模式:

当想要对已有的对象进行功能增强时,可以定义类,将已有对象传入,基于已有的功能,并提供加强功能

那么自定义的该类称为装饰类,装饰类通常会通过构造方法接受被装饰的对象

并基于装饰的对象的功能,提供更强的功能

*/

class Person

{

   public void chifan()

   {

       System.out.println("吃饭");

   }

}

class SuperPerson

{

   private Person p;

   SuperPerson(Person p)

   {

       this.p = p;

   }

   public void superChifan()

   {

       System.out.println("洗手");

       p.chifan();

   }

}

class PersonDemo

{

   public static void main(String[]args)

   {

       Person p = new Person();

       SuperPerson sp = newSuperPerson(p);

       sp.superChifan();

   }

}

装饰类和继承的区别

//继承

MyReader//专门用于读取数据的类

   |--MyTextReader

       |--MyBufferTextReader

   |--MyMediaReader

       |--MyBufferMediaReader

   |--MyDataReader

       |--MyBufferDataReader

//装饰类

class MyBufferReader

{

   MyBufferReader(MyTextReader text)

   {}

   MyBufferReader(MyMediaReadermedia)

   {}

}

上面这个类扩展性很差

找到其参数的共同类型通过多态的形式可以提高扩展性

//新装饰类

class MyBufferReader extends MyReader

{

   privateMyReader r;

   MyBufferReader(MyReaderr)

   {}}

//装饰

MyReader//专门用于读取数据的类

   |--MyTextReader

   |--MyMediaReader

   |--MyDataReader

   |--MyBufferReader

装饰模式比继承要灵活,避免了继承体系的臃肿

而且降低了类与类之间的关系

装饰类因为增强已有对象,具备的功能和已有的是相同的,只不过提供了更强功能

所以装饰类和类修饰类通常都是属于一个体系中的。

流操作的基本规律:

最痛苦的就是流对象有很多,不知道该用哪一个?

两个明确:

1,明确源和目的

   源:输入流 InputStream  Reader

   目的:输出流 OutputStream Writer

2,明确操作的数据是否是存文本

   是:字符流

   不是:字节流

3,当体系明确后,再明确要使用哪个具体对象

   通过设备进行区分

   源设备:内存,硬盘,键盘

   目的设备:内存,硬盘,控制台

举例:

1,将一个文本文件中数据存储到另一个文件中,复制文件

   源:因为是源,所以使用读取流 InputStream Reader

   是不是操作文本文件?

   是,这是就可以选择Reader

   这样体系就明确了

   接下来明确要使用该体系中的那个对象

   明确设备:硬盘  一个文件

   Reader体系中可以操作文件的对象是 FileReader

   是否需要提高效率:是,加入Reader体系中的缓冲区 BufferedReader

   FileReader fr = new FileReader("a.txt");

   BufferedReaderbufr = new BufferedReader(fr);

   目的OutputStream Writer

   是否是纯文本呢

   Writer

   设备硬盘 一个文件

   Writer 体系中可以操作文件的对象FileWriter

   是否需要提高效率加如Writer体系中的缓冲区BufferedWriter

   FileWriter fw= new FileWriter("b.txt");

   BufferedWriterbufw = new BufferedWrite(fw);

 

-------------------------------------------------------------


2,需求:将键盘录入的数据保存到一个文件中

这个需求中有 源 和 目的 都存在

那么分别分析

源:InputStream Reader

   是不是纯文本?是 Reader

设备:键盘,对应的对象是System.in

不是选择Reader(字符流)吗?System.in对应的不是字节流吗?

为了操作键盘的文本数据方便,转成字符流。按字符串操作是最方便的

所以既然明确了Reader,那么就将System.in转成Reader

用了Reader体系中转换流,InputStreamReader

InputStreamReader isr new InputStreamReader(System.in);

需要提高效率吗需要 BufferedReader

BufferedReader bufr = new BufferedReader(isr);

目的OutputStream Writer

   是否是纯文本 Writer

设备:硬盘 一个文件  使用FileWriter

FileWriter fw = new FileWriter ("c.txt");

需要提高效率吗需要

BufferedWriter bufw = new BufferedWriter(fw);

-----------------------------------------------------------------------------------

扩展

想要把录入的数据按照指定的编码表utf-8),将数据存到文件中

目的OutputStream Writer

   是否是纯文本 Writer

设备硬盘 一个文件 使用 FileWriter

FileWriter使用的是默认编码表GBK,但是存储时,需要加入指定的编码变,只有转换流可以指定

所以要使用的对象是OutputStreamWriter(是字符流通向字节流的桥梁)

而该转换流对象要接受一个字节输出流,而且还可以操作文件的字节输出流

OutputStreamWriter osw = newOutputStreamWriter(new FileOutputStream("d.txt"),"UTF-8")//指定编码表

需要高效吗?需要

BufferedWriter bufw = newBufferedWriter(osw);

所以记住,转换流什么时候使用呢,字符和字节之间的桥梁,通常涉及到字符编码转换时,需要用到转换流。


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





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值