为什么学流:流这个东西在Java中还是比较重要的,没有这个东西之前还只是自己和自己玩,也就是跑程序都在内存中,有了流之后就可以持久化在别的地方了,这就是它出现的意义
在以后的实际项目中其实基本接触不到,因为框架都已经帮你封装好了,但是这个是提升自己水平的一个核心,因为你的认知将会从我怎么将数据存入文件--->我怎么更快,更好,更安全的存入文件。正如实现一个功能并不难,难的是怎么优化,优化需要什么,那就需要基础知识,这就是流的重要性。
那什么是流?
听的高大上其实也就一句话:数据在两设备间的传输就称为流。流的本质就是数据传输。官方一点就是流是一组有顺序的,有起点和终点的字节集合,是对数据传输的总称或抽象。
流怎么分类?
稍微了解一点流的都知道,流其实种类很多,其实知道每个流是什么其实并不难,难得是你用的时候选择哪一个,那么一个良好得分类能更好的帮助你去理解。
1、按流的方向不同:方向嘛,无非就是进进出出的,所以可以分为输入流和输出流。
记住一个点,以程序为中心。输入流就是文件——>程序(进程序里面)
反之输出流就是程序——>文件(从程序出来)
2、按流的功能不同:功能就是低级和高级。官方叫做节点流和处理流
节点流怎么理解,直接与程序相连,数据直接传输,就好比没有中介插入
之间光秃秃,所以叫低级流,也叫节点流
处理流怎么理解,在节点流的基础上,在套一层,节点流不快,套上处理流就很快了。所谓在节点流的基础上,那么就表示它不能单独使用,必须搭配节点流使用。也可以这么理解,你找房子不好找,通过中介就好找许多了。
之间加宽通道,数据传输更快
按处理的数据单位不同:就是我数据传输用什么去传的,除了字符就是字节,所以可分为字符流和字节流
字节流:代表由字节组成,主要处理二进制数据。
什么流是字节流?只要流的名字中带有InputStream,OutputStream就都是。他们是所有字节流的父类。
字符流:传输的数据为文本,字符流由字符组成。
什么流是字符流?只要流的名字中带有Reader,Writer就都是。他们是所有字符流的父类。
进阶知识:当字符流读到了一个或多个字节的时候,先去查指定的编码表,将查到的字符返回。不同的字符所占的字节是不一样的。
ASCII码
一个英文字母(不区分大小写)占一个字节的位置,一个中文汉字占两个字节的位置。一共规定了 128个字符的编码,最前面1位统一为0。
2: Unicode编码
Unicode是一个很大的集合,现在的规模可以容纳 100多万个符号。
需要注意的是:Unicode只是一个符号集,他只规定了符号的二进制代码,却没有规定这个二进制代码应该如何储存
3: UTF-8编码
一个英文等于一个字节,一个中文(含繁体)等于 三个字节
UTF-8(字符可以用 1-4个字节表示)是互联网使用最广的一种Unicode的实现方式。比如还有UTF-16(字符可以用 两个或四个字节表示),和UTF-32(字符用 四个字节表示)这俩不经常使用。UTF-8是Unicode的实现方式之一
字符流和字节流的区别
字节流可以处理所有类型的数据,如:图片,MP3,AVI视频文件等。
而字符流只能处理字符数据。
所以只要是处理纯文本数据,就要优先考虑使用字符流,除此之外都使用字节流。
字节流的应用
文件流:
文件流是一对低级流,用于读写文件数据。
有两种创建形式
FileOutputStream(String path)
FileOutputStream(File file)
以上两种模式时覆盖写模式,即要操作的文件已经存在
会先将该文件数据清除,然后通过该写出的数据作为文件数据
FileOutputStream(File file,boolean append)
FileOutputStream(String path,booelan append)
当第二个数为true时,该流为追加模式,即保留文件原有数据,通过流写出的数据追加到文件后面继续写
读的方式可以是一个一个字节读,也可以是一块一块读
复制一个文件有两种方式
1:单个字节复制
2:块读写复制
3:使用高效字符流复制(利用缓冲流复制,实质也是块读写)
缓冲流
缓冲流是一对高级流,用于加快读写效率
BufferedInputStream
BufferedOutputStream
读一次的字节量为8k(8*1024)
需要注意的是:使用缓冲流(BufferedXXStream)读写数据时,读数据会使效率变快,但是写数据的时候,要是不够8k那么他将不会写入文件,这时候你得调用flush()方法,强行写入。flush使用次数与读写的效率成反比。
转换流和字符流
转换流也是字符流,用于连接字节流
InputStreamReader是字节流通向字符流的桥梁
OutputStreamWriter是字符流通向字节流的桥梁
怎么讲呢,读的时候就是将文件中的字节读出来成为字符让我们看见
而写的时候就是将一些字符转换为字节进行存储
转换流可以将字节转换为字符的原因在于,将获取到的字节通过查编码表获取到指定字符。转换流最强功能就是基于字节流+编码表。没有转换,没有字符流。
转换流是一对高级流,同时也是常用的字符流实现类。
他读写文本数据时,使用高级流(接fos)进行流连接中时非常重要的一环,起到承上启下的作用。
因为所有的字符流都只能连接在其他的字符流上,而基本的字节流都是低级流,由于转换流可以连接字节流,而本身又是字符流,所以起到字符流与字节流”对接”的作用。
括号中除了可以有一个流外,还可以指定一个编码集
BufferedReader:字符缓冲流,从字符输入流中读取文本,缓冲各个字符,从而实现字符、数组和行的高效读取
BurferedReader有一个特有的方法readline()他会按行读
BufferedWriter:字符缓冲流,将文本写入字符输出流,缓冲各个字符,从而提供单个字符、数组和字符串的高效写入。
BufferedWriter有一个特有的方法newline()写入一个分隔符
打印流
字符打印流PrintWriter和字节打印流PrintStream
与其他输出流不同打印流永远不会抛出IOException,它产生的IOException会被自身的函数所捕获并设置错误标记,可以通过checkError()返回错误标记,从而查看打印流内部是否产生了IOException
PrintWriter具有自动行刷新的缓冲字符输入流,内部总会连接BufferedWriter作为缓冲操作
比如向一个txt文件中以GBK编码格式写出字符串
Print()和println()的区别在于println会自动刷新(flush)并且换行
想要写入效率提高,你可以将他套在缓冲流中,再将缓冲流套在转换流中在放入一个文件低级流。
因为:文件中保存的是字节,需要一个转换流将字节转化为字符,在加上一个缓冲流提高速度,最后再用打印流写入。代码如下
Printstream继承于FilterOutputStream。
大家都知道system.out.println()吧,他就是把你所输入到的内容打印到控制台上,利用printstream可以把你所输入的内容打印到别的地方。如图
在这里强调一下flush()的作用:
上面的代码如果注释掉flush和close的话只会输出一个hello而不会输出writer close
因为在关闭流的同时还会调用一次flush()
总的 来说,只要调用了flush他就会行刷新,在打印流后面加一个true(前提是前面是流不是文件)会实现自动行刷新的功能,如图
序列化流(对象流)
ObjectOutputStream序列化流
ObjectInputStream反序列化流
序列化:把对象作为一个整体按照流一样的方式传输或者存储。通俗来说就是将数据结构或对象转换成二进制串的过程
反序列化:把网络中的流数据或者文件中的流还原成对象。通俗来说就是将在序列化过程中所生成的二进制串转换成数据结构或者对象的过程
什么时候需要进行序列化?
当我们需要把对象的状态信息通过网络进行传输,或者需要将对象的状态信息持久化,以便将来使用时都需要把对象进行序列化
Serializable接口
当一个类实现了Serializable接口后,应当定义一个常量:serialVersionUID这个常量时序列化版本号。若不指定,编译器会在编译时按照类的结构生成一个版本号。但是若类的结构改变,版本号会随之改变。序列化版本号直接影响对象输入流进行反序列化是否能够成功。当序列化的对象对当前类版本号一致,那么的话序列化成功,否则反序列化会发生异常若当前类结构发生了变化,只要版本号没有改变,那么反序列化时会仍然将有的属性还原
对象流
对象流是一对高级流,可以方便我们读写Java中的任何对象
对象输出流
可以指定的对象转换为一组字节后写出
对象输入流
可以将一组字节还原为相应对象,前提是这组字节应当是对象输出流将一个对象转换的字节
当一个类的实例要被对象流进行读写时,要求必须实现serializable接口
如果你不想将对象中的某个值写入磁盘那么使用transient修饰,使用这个关键字可以达到对象序列化的瘦身操作
两个对象的特有方法
ObjectInputStream
Object readObject():该方法抛出异常:ClassNotFountException。
ObjectOutputStream
void writeObject(Object)被写入的对象必须实现一个接口:Serializable 否则会抛出:NotSerializableException
读:
写: