概述
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
IO流体系:
字符流:
Reader
|--BufferedReader:
|--LineNumberReader
|--CharArrayReader
|--StringReader
|--InputStreamReaer
|--FileReader
Writer
|--BufferedWriter
|--CharArrayWriter
|--StringWriter
|--OutputStreamWriter
|--FileWriter
|--PrintWriter
字节流:
InputStream
|--FileInputStream:
|--FilterInputStream
|--BufferedInputStream
|--DataInputStream
|--ByteArrayInputStream
|--ObjectInputStream
|--SequenceInputStream
|--PipedInputStream
OutputStream
|--FileOutputStream
|--FilterOutputStream
|--BufferedOutputStream
|--DataOutputStream
|--ByteArrayOutputStream
|--ObjectOutputStream
|--PipedOutputStream
|--PrintStream
RandomAccessFile:
File:
IO流:
IO流用来处理设备之间的数据传输
按照流向流可以分为:
输出:核心设备数据往外围设备走,核心设备需要写数据
输入:外围设备数据往核心设备走,核心设备需要读数据
对内存而言——输入和输出都是相对内存而言的
Input:硬盘数据——>内存
Output:内存——>硬盘
持久化存储——断电后数据还在
硬盘:外围设备,旋转——寻道,慢
内存:数据的临时运算区,寻址,快
Java对数据的操作是通过流的方式来完成
实现了数据的流动
Java用于操作流的对象都在IO包中
流按照操作数据分为两种:字节流与字符流
字符流是基于字节流+编码表,更便于操作文字数据(读两个字节,查表,获得对应的字符)
只要是操作字符数据,应该优先使用字符流
IO流常用基类
字节流的抽象基类
InputStream , OutputStream
字符流的抽象基类
Reader , Writer
子类的阅读技巧:
流体系中的子类对象们,都以自己所属的体系名作为子类的后缀名
前缀名都代表其功能,也可以称为设备名称
close()操作跟flush()操作的区别:
使用完了windows系统资源,需要关闭资源
fw.close();//关闭资源操作close(),会先刷新flush(),再释放资源
flush()和close都可以将缓冲区中的数据写入目的地
但是flush()刷新之后,流还在,close()刷新后,流关闭
读取的两种方式:
read();
read(char[] arr);比第一种方式高效
char[] buf=new char[1024];
int len=0;
whlie(len=fr.read(buf)!=-1){
System.out.println(new String(buf,0,len));
}
练习:将c盘的一个文本文件复制到d盘。
ctrl+c——>相当于把数据存进缓冲区,也就是循环读写操作
word是非纯文本文件,所以不能直接读写。
字符到字节是写,字节到字符是读!
缓冲区:
性能优化——>缓冲!
缓冲区的出现提高了对数据的读写效率。
——字符数组缓冲区,或者字节流数组缓冲区
缓冲区就是把对应的数组进行封装,对外提供方法!
缓冲区相当于中转站,起到全程加速的作用!本身不是流!
没有流对象,缓冲区是没有意义的!
高效的写入:写入硬盘是比较慢的,所以频繁写入硬盘是比较慢的。数组是在内存中的,所以写入数组较快!
BufferedWriter BufferedReader
BufferedWriter bfw=new BufferedWriter(new FileWriter("c:\Demo.txt"));
BufferedReader bfr=new BufferedReader(new FileReader("c:\Demo.txt"));
BufferedReader的低层原理:
缓冲区中有一个数组,通过流对象把数据读到该数组之中,之后的BufferedReader读数据都是从数组之中读取数组——读单个字符,读行!
缓冲区原理对单个字符读入read()和行读入readline()有意义!对数组读入,缓冲意义不大!数组读入涉及到字节流的操作!
缓冲的存在可以使得进行更细致的操作成为可能!比如判断行分隔符——行读入
也可以进行其他的自定义操作,比如把所有的'S'字符换成'$s$'等等!
缓冲区用到的设计模式:
解决问题行之有效的办法!——设计模式
装饰设计模式:对一组对象进行功能的增强,职责的增强,还不改变原有对象!
在原有对象上增加新功能!
前期为了扩展功能,又不改变原来对象(改变定义好的类是很麻烦的),使用的是继承并复写方法来解决的。
————装饰相比于继承更加灵活
记住:装饰设计模式,装饰类和被装饰类必须属于一个体系
在原来基础上增强其功能,但功能本身不变
FileReader低层已经会存在字节缓冲区,那BufferedReader意义何在?
字节流:
InputStream OutputStream
读写操作的思想是一致的!
字节流的缓冲区:
4种复制的方式:
1,用数组作为自定义缓冲区复制
2,获取文件大小,建立刚刚好的数组一次性复制
3,使用已有的缓冲区BufferedOutputStream,BufferedInputStream
4,不使用缓冲区,字节来复制——最慢
字符流操作的结果肯定是编码表编码过之后的数据,所以它不能操作媒体文件!
转换流:
字节流关联字符文件:
方式1:
FileInputStream fis =new FileInputStream("d:\1.txt");
byte[] buf=new byte[4];
fis.read(buf);
System.out.println(new Stri
ng(buf));
方式2:
如果能使用字符流的read方法,就能解决问题!
InputStreamReader isr=new InputStreamReader(fis);
//查当前系统默认的编码表
int ch=isr.read();//得到字符
直接已知子类:
FileReader
public class InputStreamReader
extends Reader
InputStreamReader 是字节流通向字符流的桥梁:它使用指定的 charset 读取字节并将其解码为字符。它使用的字符集可以由名称指定或显式给定,或者可以接受平台默认的字符集。
每次调用 InputStreamReader 中的一个 read() 方法都会导致从底层输入流读取一个或多个字节。要启用从字节到字符的有效转换,可以提前从底层流读取更多的字节,使其超过满足当前读取操作所需的字节。
为了达到最高效率,可要考虑在 BufferedReader 内包装 InputStreamReader。例如:
BufferedReader in = new BufferedReader(new InputStreamReader(System.in));
FileInputStream fis=new FileInputStream("d:\1.txt");
InputStreamReader isr = new InputStreamReader(fis,"GBK");
FileInputStream fis=new
父子有什么区别?
FileReader是一个操作文本文件的使用默认编码的便捷类,它将字节流跟编码表进行了封装!
如果操作的不是文件,或者使用的不是默认编码,就不能使用FileReader,只能使用转换流!因为转换流可以指定要用的编码表,操作的数据可以是字节流
字符流关联字节流
System.in
System.out作为数据源或者数据目的
流操作规律:——流可以处理数据,但是处理数据的流对象很多,都有各自的特点和功能!到底如何选择最合适的对象呢?操作规律来确定
流的操作无非读写,所以通过几个明确就可以具体确定该使用那个对象:
1,确定是源还是目的——确定读还是写!
源:InputStream,Reader
目的:OutputStream Writer
2,处理的数据是否是纯文本数据——确定了具体体系
是纯文本,源:Reader
是纯文本,目的:Writer
不是纯文本——字节流
3,找体系中的具体对象——明确具体流对象
要确定的是数据所在的具体设备:
源:硬盘(File前缀的流对象),键盘(System.in),内存(数组),网络(Socket)
目的:硬盘(File前缀的流对象),显示器/控制台(System.out),内存(数组),网络(Socket)
4,明确具体对象后,继续明确是否需要额外功能
高效:Buffered
转换编码:InputStreamReader,OutputStreamWriter桥梁
Unicode跟本地编码转换:
打开文件看到的是文字!!!???
这内部应该有自动转换,让显示出来的是字符形式!
File类:
数据最常见的形式——文件!
File类将文件或者文件夹封装成了对象,便于对文件或者文件夹进行操作
提供了n多种方法对其进行操作
既可以封装已经存在的文件也可以封装不存在的文件或者文件夹
文件类型是由解析软件来说明的!文件可以没有扩展名(标示其文件类型的)
属性,字段:
路径分隔符:File.pathseparator
文件分隔符:File.separator
行分隔符:System.getProperty("line.separator");
方法:
获取:名称,大小,路径
修改时间:判断文件在当前程序操作过程中是否被修改,单独一个线程来监视文件是否被修改(获取时间跟修改时间是否一致),修改了则加载
创建,删除
f1.renameTO(f2):剪切并重命名
如果File对象中封装的文件夹不存在,或者封装的是个文件,list返回的都是null,所以在使用该方法时,建议加上判断,exists()&&isDirectory()
String[] names=dir.list();//列出当前目录的所有文件和文件夹,包含隐藏,不包含子目录
File[] files=dir.listFiles();//把文件作为对象装到数组返回,常用
过滤器的原理:过滤名字FilenameFilter,过滤文件FileFilter
函数的递归:——列出一个目录下所有文件及文件夹,包含所有子文件和子文件夹
直接调用自己,间接调用自己
前提:
1,必须要定义结束条件——如果没有结束条件会导致栈内存溢出
2,一定要控制递归的次数——次数太多仍然会造成栈内存溢出
什么时候使用递归:当一个功能被重复使用,但是每次调用时,传入的参数都和上一次运算结果相关
Properties类:
IO技术跟map集合相结合
是可以持久化的属性值的集合,可以将集合中的数据存储到持久化设备中,所以需要IO技术
该集合中的键和值都是字符串类型
该集合可以把数据存储刀到持久化设备上,可以将持久化设备上的规范数据存储到集合中
配置文件有两种:
properties可以进行简单属性配置
xml可以进行复杂描述——DOM4J:DOM for Java来解析配置文件
IO流之中的其他功能对象:——具备特定的功能
1,打印流:
PrintWriter与PrintStream
可以直接操作输入流和文件
int read():一次读取一个字节,在转成int型
write(97)将一个整数思维中的最低位字节写入目的地
记事本显示:a
print(97)——把数据的表示形式打印到目标中,目的的数据和打印时的数据表现形式一致。给的什么,打印出来就是什么
内部是写入的String.valueOf(97);
记事本显示:97('9','7'两个字节)
键盘录入和显示输出是系统的标准输入输出,System.in和System.out在系统中是唯一的,在程序中关闭了该流之后,后面代码再也不能使用这两个流。除非再次启动程序。
想要保证数据值的表示形式时,使用该额外功能
2,序列流SequenceInputStream
有序排列 ——
多个源对应一个目的
内部封装了一个集合,也是在操作一个有序集合,多个流对象合并成一个流
序列流构造函数中需要枚举类型Enumeration,所以要用Vector来作为流对象的容器。但是这已经不用了,可以自己建立枚举对象,然后重写其中方法(必须先建立ArrayList的迭代器,用迭代器中的代码实现枚举中的功能);也可以用Collections之中的方法,把这个ArrayList转换成对应的枚举类型
练习:文件分割器
分割完之后再还原
3,对象的序列化和反序列化
操作对象ObjectInputStream与ObjectOutputStream
bean数据模型,很多方法,很多属性,很多对象
对象序列化:将多个对象按照顺序进行排序并持久化,一个一个存进去序列化会在每一个对象之后添加一个结束标记,用来跟下一个对象进行区别。
对象在程序结束之后需要该对象还在,延长对象的生命周期,持久存储
反序列化:把存储的对象一个一个读出来
需要强转
Serializable接口就是个标记,内部没有自己的方法
数据签名:用来唯一标示(类或者对象)的标示符
要把对象持久化存储,该对象必须实现接口Serializable;
对象中的数据是特有的,用class文件可以再次创建该类对象,但是属性就不一样了,就不是原来的对象了!
就比如你玩游戏的人物一样,你玩游戏玩快满级了,死机重启之后,如果没有原来对象的话,你进去就是0级!所以需要.class文件和对象文件才能得到原来的对象。
Serializable接口到底做了什么?
该接口目的就是给每一个需要序列化的类添加一个ID,防止反序列化的时候不匹配!每个类都有一个serialVersionUID,改变了类之中的内容,该序列号也就随之改变。
所以该ID号强烈建议自定义并私有!因为默认算法对类的内容敏感性很高,微小的变动就会导致该ID号不一致InvalidClassException,从而加载失败!
静态的数据不会被序列化,因为静态数据存在于方法区,它不属于对象;
对于非静态数据如果也不想被序列化,可以用transient(短暂的)来修饰,表示这个数据不是一个生命周期很长的数据,所以不要序列化来持久存储
文件中存储什么东西,就叫什么扩展名,存Object就叫.Object
练习:把多个类对象写入相同文件,读出来的时候,怎么读?
把不同类的对象写入相同文件,读出来怎么读?
4,管道流PipedInputStream和PipedOutputStream
以前输出和输入流之间需要中转站,
1,而管道流输入流和输出流可以连接
2,必须结合多线程技术,单线程容易造成死锁
5,操作基本数据类型
DataInputStream&&&DataOutputStream
6,操作字节数组
ByteArrayInputStream与ByteArrayOutputStream
数据在内存中,读写没有用到底层资源,所以关闭无效,而且不会产生IOException
把数组封装,用流的思想来操作数组!
以字节数组作为源
相当于把一个数组中的内容写到另外一个数组之中。
7,操作字符数组
CharArrayReader与CharArrayWrite
不需要关闭资源
8,操作字符串
StringReader与StringWriter
不需要关闭资源
9,RandomAccessFile——流的工具类
随机访问文件,自身具备读写的方法,随机访问就是用指针来记录要操作的数据位置:
1,该类不是流体系中的对象
2,用来操作文件的对象
3,可以进行读写操作
4,内部定义了一个大型的字节数组
5,封装了字节流的工具类,可读写,字节数组缓冲
6,操作的源和目的只有文件
从程序往记事本中写内容,打开记事本后,修改并保存记事本中的内容,则现在内存中的数据是从记事本中写入的!这会再从记事本输出,就是记事本的内容,可能编码就改变了,得到的内容不同!
把数组封装成对象,有了数组,操作指针,实现随机访问!要尽量保证数据有规律,才能用seek能设定步长;.
完成随机访问的原理?
seek()指定指针的位置——实现随机访问的根本方法
skipBytes()方法跳过指定的字节数
getFilePointer()获取指针的位置
只能操作文件,四种访问方式
应用场景:3G可用
能实现多线程同时往一个文件中写数据,指定开始和结束
----------- android培训、java培训、java学习型技术博客、期待与您交流! ------------
最后
以上就是长情鸡翅为你收集整理的黑马程序员:IO流 IO流体系:IO流:的全部内容,希望文章能够帮你解决黑马程序员:IO流 IO流体系:IO流:所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复