------- android培训、java培训、期待与您交流! ----------
IO(Input Output)流
IO流用来处理设备之间的数据传输
Java对数据的操作是通过流的方式
Java用于操作流的对象都在IO包中
IO流常用基类
字节流的抽象基类
InputStream OutputStream
字符流的抽象基类
Reader Writer
注:有这四个类派生出来的子类名称
都是以其父类作为子类名称的后缀。
如:InputStream的子类FileInputStream
如:Reader的子类FileReader
IO流分类
字节流:
为了方便理解,可以把InputStream和OutputStream比作两根“水管”。
InputStream的常用方法
intread()从输入流读取一个8位的字节,把它转换为0—255之间的整数,并返回这一整数
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();
}
字节流缓冲区
BufferedInputStream和BufferedoutputStream
//通过字节流的缓冲区完成复制
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培训、期待与您交流! ----------