8.3.3 字符输出流
package cn.it.bz.IO; import java.io.IOException; import java.io.PrintWriter; public class TestPrintWriter { public static void main(String[] args) { //创建字符流输出对象 try(PrintWriter printWriter = new PrintWriter("D:/a.txt")) { printWriter.print("Java");//不换行 printWriter.print("百战"); printWriter.println("sxt");//换行 printWriter.println("哈哈哈"); printWriter.flush(); }catch (IOException e){ e.printStackTrace(); } } }
小练习:通过字符输出流添加行号
package cn.it.bz.IO; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.PrintWriter; public class TestLineNumber3 { public static void main(String[] args) { try( //读入一行数据数据 BufferedReader bufferedReader = new BufferedReader(new FileReader("D:/a.txt")); //输出数据 PrintWriter printWriter = new PrintWriter("D:/a1.txt")) { int i = 1; String s = ""; while ((s = bufferedReader.readLine()) != null){ printWriter.println(i+"、"+s); i++; } //释放资源 printWriter.flush(); }catch (IOException e){ e.printStackTrace(); } } }
8.4 数据流
数据流将“基本数据类型与字符串类型”作为数据源,从而允许程序以与机器无关的方式从底层输入输出流中操作Java基本数据类型与字符串类型。数据流是处理流,不是节点流,而且只能处理文件字节流,不能处理文件字符流。
package cn.it.bz.IO; import com.sun.org.apache.xerces.internal.impl.dv.dtd.IDREFDatatypeValidator; import java.io.*; public class TestDataOutputStream { public static void main(String[] args) { //先向文件写数据,在从文件获取数据。 try(DataOutputStream dataOutputStream = new DataOutputStream(new FileOutputStream("D:/a.txt")); DataInputStream dataInputStream = new DataInputStream(new FileInputStream("d:/a.txt")) ) { //将如下数据写到文件 dataOutputStream.writeChar('张'); dataOutputStream.writeUTF("输出字符串"); dataOutputStream.writeInt(123); dataOutputStream.writeDouble(Math.random()); dataOutputStream.writeBoolean(true); dataOutputStream.flush(); //读取顺序需要和写入顺序一样才行 System.out.println(dataInputStream.readChar()); System.out.println(dataInputStream.readUTF()); System.out.println(dataInputStream.readInt()); System.out.println(dataInputStream.readDouble()); System.out.println(dataInputStream.readBoolean()); }catch (IOException E){ E.printStackTrace(); } } }
1、使用数据流时,读取的顺序一定要与写入的顺序一致,否则不能正确读取数据。
2、数据流只能处理文件字节流,不能处理文件字符流
8.5 对象流
我们前边学到的数据流只能实现对基本数据类型和字符串类型的读写,并不能读取对象(字符串除外),如果要对某个对象进行读写操作,我们需要学习一对新的处理流:
ObjectInputStream/ObjectOutputStream。对象流实际上是数据流的升级版,使用对象流也能实现对Java中基本数据类型的读写操作,还能对自定义对象进行读写操作。对象流和数据流一样都是处理流,只能处理字节流不能处理字符流。
8.5.1 对象流处理基本数据类型
package cn.it.bz.IO; import java.io.*; public class TestObjectStreamBasicType { public static void main(String[] args) { try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:/a.txt")); ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:/a.txt")) ) { //数据写出 objectOutputStream.writeInt(123); objectOutputStream.writeChar('h'); objectOutputStream.writeBoolean(true); //刷新 objectOutputStream.flush(); //按照写入的顺序读取数据 System.out.println(objectInputStream.readInt()); System.out.println(objectInputStream.readChar()); System.out.println(objectInputStream.readBoolean()); }catch (IOException E){ E.printStackTrace(); } } }
8.5.2 对象流将对象序列化到文件
ObjectOutputStream可以将一个内存中的Java对象通过序列化的方式写入到磁盘的文件中。被序列化的对象必须要实现Serializable序列化接口,否则会抛出异常。
package cn.it.bz.IO; import java.io.*; public class TestObjectOutputStream { public static void main(String[] args) { try(ObjectOutputStream objectOutputStream = new ObjectOutputStream(new FileOutputStream("D:/a.txt")); ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("D:/a.txt"))) { //创建Users对象 Users users = new Users(123,"zhangsan","12"); //将对象序列化到文件 objectOutputStream.writeObject(users); //刷新 objectOutputStream.flush(); }catch (IOException e){ e.printStackTrace(); } } }
如果想一次性将多个对象序列化到文件中,可以将对象放在集合中,集合实现了序列化接口。
8.5.3 将对象反序列化到内存中
package cn.it.bz.IO; import java.io.FileInputStream; import java.io.ObjectInputStream; public class TestObjectInputStream { public static void main(String[] args) { //创建对象输入字节流与文件字节输入流对象 try(ObjectInputStream objectInputStream = new ObjectInputStream(new FileInputStream("d:/a.txt"))){ //将对象反序列化到内存中 Users users = (Users) objectInputStream.readObject(); System.out.println(users); }catch(Exception e){ e.printStackTrace(); } } }
九、File类在IO中的作用
File是操作文件的,当以文件作为数据源或目标时,除了可以使用字符串作为文件以及位置的指定以外,我们也可以使用File类指定。
package cn.it.bz.IO; import java.io.*; public class TestFile { public static void main(String[] args) { try(BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(new FileInputStream(new File("D:/a.txt")),"gbk"))) { String s =""; while ((s = bufferedReader.readLine()) != null){ System.out.println(s); } }catch (IOException e){ e.printStackTrace(); } } }
十、装饰器模式构建IO流体系
10.1 装饰器模式简介
装饰器模式是GOF23种设计模式中较为常用的一种模式。它可以实现对原有类的包装和装饰,使新的类具有更强的功能。
package cn.it.bz.IO; class Iphone{ private String name; public Iphone(String name) { this.name = name; } public void show() { System.out.println("我是" + name + ",可以在屏幕上显示"); } } //装饰器 class TouyingPhone { public Iphone phone; public TouyingPhone(Iphone p) { this.phone = p; } // 对Iphone功能的扩展 public void show() { phone.show(); System.out.println("还可以投影,在墙壁上显示"); } } public class TestDecoration { public static void main(String[] args) { Iphone iphone = new Iphone("苹果18"); //将有投影能力的手机壳装到手机上 TouyingPhone touyingPhone = new TouyingPhone(iphone); touyingPhone.show(); } }
10.2 IO流体系中的装饰器模式
IO流体系中大量使用了装饰器模式,让流具有更强的功能、更强的灵活性。比如:
FileInputStream fis = new FileInputStream(src);
BufferedInputStream bis = new BufferedInputStream(fis);显然BufferedInputStream装饰了原有的FileInputStream,让普通的FileInputStream也具备了缓存功能,提高了效率。
十一、Apache IO包
Apache基金会介绍
Apache软件基金会(也就是Apache Software Foundation,简称为ASF),是专门为支持开源软件项目而办的一个非盈利性组织。在它所支持的Apache项目与子项目中,所发行的软件产品都遵循Apache许可证(Apache License)。 官方网址为:www.apache.org
很多著名的Java开源项目都来源于这个组织。比如:commons、kafka、lucene、maven、shiro、struts等技术,以及大数据技术中的:hadoop(大数据第一技术)、hbase、spark、storm、mahout等。
commons-io工具包
Apache的commons-io工具包中提供了IOUtils/FileUtils,为我们提供了更加简单、功能更加强大的文件操作和IO流操作功能。
下载与添加commons-io包
下载地址
Commons IO – Download Apache Commons IO
在项目中创建lib文件境jar包拖入到该文件中,然后右键lib选择Add as Library
11.1 FileUtils类中常用方法的介绍
打开FileUtils的api文档,我们抽出一些工作中比较常用的静态方法,进行总结和讲解。总结如下:
方法名 | 使用说明 |
cleanDirectory | 清空目录,但不删除目录 |
contentEquals | 比较两个文件的内容是否相同 |
copyDirectory | 将一个目录内容拷贝到另一个目录。可以通过FileFilter过滤需要拷贝的文件 |
copyFile | 将一个文件拷贝到一个新的地址 |
copyFileToDirectory | 将一个文件拷贝到某个目录下 |
copyInputStreamToFile | 将一个输入流中的内容拷贝到某个文件 |
deleteDirectory | 删除目录 |
deleteQuietly | 删除文件 |
listFiles | 列出指定目录下的所有文件 |
openInputSteam | 打开指定文件的输入流 |
readFileToString | 将文件内容作为字符串返回 |
readLines | 将文件内容按行返回到一个字符串数组中 |
size | 返回文件或目录的大小 |
write | 将字符串内容直接写到文件中 |
writeByteArrayToFile | 将字节数组内容写到文件中 |
writeLines | 将容器中的元素的toString方法返回的内容依次写入文件中 |
writeStringToFile | 将字符串内容写到文件中 |
11.1.1使用FileUtils工具类读取文件
package cn.it.bz.IO; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.IOException; public class TestUtils1 { public static void main(String[] args) throws IOException { //content表示读取的文件中的全部的内容 String content = FileUtils.readFileToString(new File("D:/a.txt"), "gbk"); System.out.println(content); } }
11.1.2使用FileUtils工具类实现目录拷贝
我们可以使用FileUtils完成目录拷贝,在拷贝过程中可以通过文件过滤器(FileFilter)选择拷贝内容。
package cn.it.bz.IO; import org.apache.commons.io.FileUtils; import java.io.File; import java.io.FileFilter; import java.io.IOException; public class TestFileUtils2 { public static void main(String[] args) throws IOException { FileUtils.copyDirectory(new File("D:\\图片"), new File("d:/aaa"), new FileFilter() { //给定文件拷贝规则,只拷贝以png结尾的文件 @Override public boolean accept(File pathname) { if (pathname.getName().endsWith(".png")){ return true; //拷贝 } return false; //不拷贝 } } ); } }
11.2 IOUtils的妙用
打开IOUtils的api文档,我们发现它的方法大部分都是重载的。所以,我们理解它的方法并不是难事。因此,对于方法的用法总结如下:
方法名 | 使用说明 |
buffer | 将传入的流进行包装,变成缓冲流。并可以通过参数指定缓冲大小 |
closeQueitly | 关闭流 |
contentEquals | 比较两个流中的内容是否一致 |
copy | 将输入流中的内容拷贝到输出流中,并可以指定字符编码 |
copyLarge | 将输入流中的内容拷贝到输出流中,适合大于2G内容的拷贝 |
lineIterator | 返回可以迭代每一行内容的迭代器 |
read | 将输入流中的部分内容读入到字节数组中 |
readFully | 将输入流中的所有内容读入到字节数组中 |
readLine | 读入输入流内容中的一行 |
toBufferedInputStream,toBufferedReader | 将输入转为带缓存的输入流 |
toByteArray,toCharArray | 将输入流的内容转为字节数组、字符数组 |
toString | 将输入流或数组中的内容转化为字符串 |
write | 向流里面写入内容 |
writeLine | 向流里面写入一行内容 |
package cn.it.bz.IO; import org.apache.commons.io.IOUtils; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class TestIOUtilsDemo { public static void main(String[] args) throws IOException { String content = IOUtils.toString(new FileInputStream("D:/a.txt"), "gbk"); System.out.println(content); } }
实际上就是相当于文件字节输入流套上了文件字符转换流(并指定解码规则)再套上文件字符缓冲流。