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包直接导入即可使用. */
9.12各种IO流
最新推荐文章于 2024-08-18 21:36:31 发布