9.12各种IO流

P161-P167
/*
缓冲流
之前学的字符字节输入输出流都属于原始流,运行效率较低,所以需要学习更高效的流,也就是缓冲流
之前的原始流等于是创建一个管道来运水,每次接收一滴或者一桶水
而缓冲流会在创建管道以后创建一个缓冲区,先把水放进一个大水箱,要取水的时候再从水箱中拿,这样效率就比直接从管道拿高很多,实际上每次加载是8kb
缓冲流和原始流相对应,也是有四种. 他们也是inputStreamoutputStream和ReaderWriter的实现类,名字就是在前面加上buffered
由于bufferedinputStream和fileInputStream一样都是inputStream的实现类,所以api大致类似,但是创建对象的过程是有所不同的

字节缓冲流
要想创建缓冲流,需要有一个原始的流作为原料,给他包装成一个缓冲流. 需要先创建一个原始流:
InputStream inputStream = new FileInputStream("源文件");
再把原始流入参进去,生成buffered缓冲流
InputStream iS = new BufferedInputStream(inputStream);
这个缓冲流的效率已经比原始流高出很多了,再用这个流结合数组(byte[])的方式来操作文件,速度可以达到飞快
    //首先还是创建文件对象来获取流
    File f = new File("E:\\源文件.zip");
    File d = new File("E:\\复制的文件.zip");
    long start = System.currentTimeMillis();
    //这个桶大一点有时候可以更快,但实际1024就已经够快了.有时候桶过大反而慢
    byte[] buffer = new byte[102444];
    try (
         //在try里加载资源,用完后会自动关闭. 先创建原始流再包装成缓冲流
         InputStream is = new FileInputStream(f);
         InputStream is1 = new BufferedInputStream(is);
         OutputStream os = new FileOutputStream(d);
         OutputStream os1 = new BufferedOutputStream(os);

    ) {
        int len;
        //用缓冲流来进行复制操作
        while ((len = is1.read(buffer)) != -1) {
            os1.write(buffer,0,len);
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    long end = System.currentTimeMillis();
    System.out.println("拷贝完成,共花费时间:" + (end - start)  + "毫秒");


字符缓冲流
字符缓冲流和字节缓冲流区别不大,创建方法都是把原始流包装成缓冲流,也就是把fileWriter包装成bufferedWriter,fileReader包装成bufferedReader
字符缓冲流也是自带8K缓冲区, 提高了原始字符流的读写性能
其中,bufferedReader提供了一个readLine()的方法,可以一起读整行代码,一般读取字符文件我们常用这种方法
bufferedWriter提供了一个newLine()的方法,可以完成换行,实际上和之前那种换行方式没差别.
之前换行是用的writer.write("\\r\n".getBytes())

当我们使用字符缓冲流的时候,可能会出现因为编码不同而出现乱码的问题. 这时候就需要用到转换流inputStreamReader以及outputStreamWriter
比如在读取的时候,我们默认是UTF-8来读取,但文件可能是GBK编码. 这时如果直接读取字符就会出现中文乱码问题
所以我们需要一个流来进行一个转换,首先我们先把文件提取成fileinputStream字节流,
File f1 = new File("C:\\Users\\Administrator\\Desktop\\新建文本文档.txt");
FileInputStream ansi = new FileInputStream(f1);
然后把字节流转化成inputStreamReader字符输入转换流,将其用GBK格式解码编码
InputStreamReader gbk = new InputStreamReader(ansi, "GBK");
再把字符转换流变成bufferedReader,即可解决乱码问题
BufferedReader bufferedReader = new BufferedReader(gbk);

同样的,在使用bufferedWriter的时候也会遇到这个问题.比如文件中需要写入GBK,而我们默认是写入UTF-8的
此时可以先获得一个字节输出流
File f2 = new File("C:\\Users\\Administrator\\Desktop\\出师表.txt");
FileOutputStream fileOutputStream = new FileOutputStream(f2);
再用这个字节输出流给他变成 字符输出转化流 outputStreamWriter
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(fileOutputStream);
最后把这个转化流包装成bufferedWriter即可高效完成写入工作
BufferedWriter bufferedWriter = new BufferedWriter(outputStreamWriter);

*/


/*
对象序列化
把内存中的对象存到磁盘文件中,称为对象序列化.  比如我现在从数据库里搜索学生对象,搜索到了100个,这100个学生对象我可能下次还要用
那我就可以把这100个学生作为对象储存到文件里,下次用的时候再反序列化即可. 因为我们从数据库调取数据是很浪费性能的,我可能调取一次
以后关闭还要调取一次, 这样不如直接把数据库调取出来的对象从内存里直接存到磁盘里,后面再去磁盘文件找就快得多了.
用到的是对象字节输出流ObjectOutputStream,这个流也是实现了outputStream的高级流

首先要存入对象,需要先创建一个对象. 比如一个学生类创建一个学生对象
Student s = new Student("张三",21,'男');
接下来需要创建ObjectOutputStream对象字节输出流,这需要一个原始的字节输出流fileOutputStream,入参一个文件路径以便后续存入对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("javasepromax\\DAY10-oop-demo\\src\\bac.txt"));
写入对象的api是writeObject()
oos.writeObject(s);
oos.close();
这样即可把一个学生对象存入文件. 但注意,一个学生对象能被序列化,前提是这个学生类必须实现了可序列化接口Serializable
打开文件,看起来感觉是一堆乱码,但实际上学生对象已经存入,只不过这个不是给程序员直接读懂的,而是用来反序列化调取的

反序列化
使用的是读取文件中对象的流,ObjectInputStream. 同样也是由一个原始流fileInputStream包装而成.
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("javasepromax\\DAY10-oop-demo\\src\\bac.txt"));
主要方法是readObject(),读取对象
Student o = (Student) ois.readObject();

增强序列化的安全性
序列化文件有可能需要交给第三方操作, 如果把对象里所有的实例成员变量都序列化,很有可能导致信息不安全.可以在类里面用transient修饰变量
被transient修饰的变量不参与序列化与反序列化 如让密码不参与序列化以防止密码泄露:
private transient String password;

序列化的版本号
对象在使用过程中不是一成不变的,可能是需要升级的. 比如对象原来只有姓名账号密码,我可能想加入性别和年龄
此时如果之前的对象已经被序列化了,那么更新以后如果存在文件中的对象再被取出,再被反序列化,取出来的数据可能就是过时的没被更新的对象了
为了避免这种情况的发生,我们可以在类里面定义一个序列化的版本号,和实例成员变量放在一起,代码如下:
private static final long serialVersionUID =1;
每次升级或者改动类的内容时,我们就可以升级版本号来让之前序列化的文件作废,不能再被反序列化(取出).
比如序列号1的时候我把很多学生对象存到了文件中,然后学生类进行了升级,升级以后版本号变成了2
此时再把文件里的类解析出来反序列化的时候,就会发现现在版本号不是1了,就不能完成反序列化了.
相当于强制让程序员去更新内容,避免程序员不知道类的更新还用以前的旧对象
 */


/*
打印流
PrintStream打印流是一种高级流,可以方便高效的把内存里的数据打印(写入)到文件中.
它的操作相当简单,可以直接定义写入的编码类型,如果是bufferedWriter还需要搞转换流,还需要inputStreamWriter写编码类型
PrintStream不但操作简单,而且效率比bufferedWriter更高
作为高级流,打印流不但可以由原始流来创建对象,也可以直接通向文件对象,由文件对象直接建立打印流
其方法print()可以打印任意类型的数据
PrintStream printStream = new PrintStream("javasepromax\\DAY10-oop-demo\\src\\bac.txt","GBK");
printStream.print("这里写什么都可以");
printStream.close();
与之相对的还有一个PrintWriter,基本上和PrintStream没什么区别.只不过一个继承自字节流,一个继承自字符流
所以唯一的不同是PrintStream可以write写入字节,而PrintWriter可以write写入字符.
但其实没什么所谓,因为PrintWriter主要是调用它的printAPI,很少调用write.

打印流还可以改变输出语句输出的位置. 其实下面这行代码就是调用了一个out打印流来完成了向控制台打印语句的功能.
System.out.println();
我们可以自己创建一个流,改变输出位置
PrintStream ps = new PrintStream("PATH");
System.setOut(ps)
通过这样的设置以后,打印语句就不会再向控制台输出语句,而是向PATH路径的文件里输出内容,这叫打印流的重定向
*/


/*
Properties
类似于之前使用logback的配置文件xml,还有很多程序使用之前需要进行配置.而properties就是用来做这个初始化配置的工作
properties其实是实现了MAP接口的一种键值对集合,专门保存配置信息的键值对. 可以直接调用无参构造器创建.
虽然是map集合的一种,但是properties因为其特殊性,已经从map家族离家出走.properties很少调用map家族的api
Properties p = new Properties();
对于properties对象,我们可以往里面存入键值对来保存配置信息. 调用的api是setProperty,但其实和map的put是没有区别的,只是为了和map独立开
p.setProperty("switch","on"); 效果等同于put
接下来我们需要把这个信息存入文件中来长久保存,可以调用store方法,这个方法需要两个参数,首先要有一个流链接文件,因为我们要写入一些字符而不是字节
所以选择了fileWriter来入参.
p.store(new FileWriter("javasepromax\\DAY10-oop-demo\\src\\保存配置信息的文件.txt"),"这里可以填写一些注释");
如果需要读取文件中的配置信息到properties对象中,可以选择load方法. 因为要读取字符,我们选择fileReader入参
Properties p1 = new Properties();
p1.load(new FileReader("javasepromax\\DAY10-oop-demo\\src\\bac.txt"));
此时p1里面就会读取之前文件中的键值对并保存.
另外记录几个properties的api
setProperty("switch","on")  效果等同于put,存入键值对
getProperty()  入参key获得value,效果等同于get
stringPropertyNames()  获取所有键的set集合,效果等于keyset
 */


/*
commons-io框架
真实开发中我们一般不会自己写底层源码,直接使用大佬开发的框架即可完成这些功能
比如复制文件,查找文件,删除文件,一行代码即可搞定.只需要百度下载commons-io的jar包直接导入即可使用.
 */
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值