一 (字符流扩展)
1 字符输出流
(更方便的输出字符——>取代了缓冲字符输出流——>因为他自己的节点流)
(PrintWriter——>节点流——>具有自动行刷新缓冲字符输出流——>可以按行写出字符串,并且可通过println();方法实现自动换行)
在Java的IO流中专门提供了用于字符输出的流对象PrintWriter。该对象具有自动行刷新缓冲字符输出流,特点是可以按行写出字符串,并且可通过println();方法实现自动换行。
public class TestPrintWriter {
public static void main(String[] args) {
//创建字符输出流对象
try(PrintWriter pw = new PrintWriter("d:/sxt5.txt")){
//调用不带换行方法完成内容的输出
pw.print("abc");
pw.print("def");
//调用带有自动换行方法完成内容的输出
pw.println("jia");
pw.println("jiajia");
pw.flush();
}catch(IOException e){
e.printStackTrace();
}
}
}
2 字符输出流添加行号
(以后有输出字符的时候——>可以直接用字符输出流(PrintWriter))
public class TestLineNumber3 {
public static void main(String[] args) {
//创建字符输入缓冲流对象与文件字符输入流对象
try(BufferedReader br = new BufferedReader(new FileReader("d:/jia.txt"));
//创建字符输出流对象
PrintWriter pw = new PrintWriter("d:/jia2.txt")){
//操作流
String temp = "";
//定义序号变量
int i = 1;
while((temp = br.readLine()) != null){
pw.println(i+","+temp);
//序号累加
i++;
}
//刷新
pw.flush();
}catch(IOException e){
e.printStackTrace();
}
}
}
二 (字节流扩展)
1 数据流
(DataInputStream/DataOutputStream——>处理流——>只能处理字节流)
(以Java基本数据类型与字符串类型——>作为输入和输出)
(读取的顺序要与写入的顺序一致)
数据流将“基本数据类型与字符串类型”作为数据源,从而允许程序以与机器无关的方式从底层输入输出流中操作Java基本数据类型与字符串类型。
DataInputStream和DataOutputStream提供了可以存取与机器无关的所有Java基础类型数据(如:int、double、String等)的方法。
先写再读
public class TestDataStream {
public static void main(String[] args) {
//创建数据输出流对象与文件字节输出流对象
try(DataOutputStream dos = new DataOutputStream(new FileOutputStream("d:/data"));
//创建数据输入流对象与文件字节输入流对象
DataInputStream dis = new DataInputStream(new FileInputStream("d:/data"))){
//将如下数据写入到文件中
dos.writeChar('a');
dos.writeInt(10);
dos.writeDouble(Math.random());
dos.writeBoolean(true);
dos.writeUTF("哈哈");
//手动刷新缓冲区:将流中数据写入到文件中
dos.flush();
//直接读取数据:读取的顺序要与写入的顺序一致,否则不能正确读取数据。
System.out.println("char: " + dis.readChar());
System.out.println("int: " + dis.readInt());
System.out.println("double: " + dis.readDouble());
System.out.println("boolean: " + dis.readBoolean());
System.out.println("String: " + dis.readUTF());
}catch(IOException e){
e.printStackTrace();
}
}
}
提示:
使用数据流时,读取的顺序一定要与写入的顺序一致,否则不能正确读取数据。
2 对象流
(ObjectInputStream/ObjectOutputStream——>处理流——>只能处理字节流)
(对象流——>数据流的升级——>多了对对象进行读写操作)
(读取的顺序要与写入的顺序一致)
(!!!读取对象——>类必须实现java.io.Serializable
接口。Serializable
接口是一个标记性接口,它不包含任何方法,但是它的实现表明该类的对象可以被序列化和反序列化!!!——>如果一个类实例化另一个对象,那么那个对象的类也必须实现Serializable
接口)
我们前边学到的数据流只能实现对基本数据类型和字符串类型的读写,并不能读取对象(字符串除外),如果要对某个对象进行读写操作,我们需要学习一对新的处理流:ObjectInputStream/ObjectOutputStream。
处理基本数据类型数据
ObjectInputStream/ObjectOutputStream处理基本数据类型。
ipublic class TestObjectStreamBasicType {
public static void main(String[] args) {
//创建对象输出流对象与文件字节输出流对象
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/data2"));
//创建对象输入流对象与文件字节输入流对象
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/data2"))){
//将如下数据写入到文件中
oos.writeInt(10);
oos.writeDouble(Math.random());
oos.writeChar('a');
oos.writeBoolean(true);
oos.writeUTF("你好");
oos.flush();
//必须要按照写入的顺序读取数据
System.out.println("int: "+ois.readInt());
System.out.println("double: "+ois.readDouble());
System.out.println("char: "+ois.readChar());
System.out.println("boolean: "+ois.readBoolean());
System.out.println("String: "+ois.readUTF());
}catch(IOException e){
e.printStackTrace();
}
}
}
3 对象的序列化和反序列化
(序列化——>对象——>二进制数据 ,反序列化——>二进制数据——>对象)
(两个进程远程通信时——>何种类型的数据,都会以二进制序列的形式在网络上传送)
(只有实现了—Serializable—接口的类的对象才能被序列化——>Serializable是个空接口只是用来标记的)
(writeObject(Object obj)方法,readObject()方法)
(让对象的某个属性不参与序列化,可以使用——> transient
修饰符)
1 序列化和反序列化是什么
当两个进程远程通信时,彼此可以发送各种类型的数据。 无论是何种类型的数据,都会以二进制序列的形式在网络上传送。比如,我们可以通过http协议发送字符串信息;我们也可以在网络上直接发送Java对象。发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象才能正常读取。
把Java对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为Java对象的过程称为对象的反序列化。
2 序列化涉及的类和接口
ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。
ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。
只有实现了Serializable接口的类的对象才能被序列化。 Serializable接口是一个空接口,只起到标记作用。
4 对象序列化到文件
(容器也实现了Serializable接口)
ObjectOutputStream可以将一个内存中的Java对象通过序列化的方式写入到磁盘的文件中。被序列化的对象必须要实现Serializable序列化接口,否则会抛出异常。
创建对象
public class Users implements Serializable { //要实现类
private int userid;
private String username;
private String userage;
public Users(int userid, String username, String userage) {
this.userid = userid;
this.username = username;
this.userage = userage;
}
public Users() {
}
public int getUserid() {
return userid;
}
public void setUserid(int userid) {
this.userid = userid;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getUserage() {
return userage;
}
public void setUserage(String userage) {
this.userage = userage;
}
@Override
public String toString() {
return "Users{" +
"userid=" + userid +
", username='" + username + '\'' +
", userage='" + userage + '\'' +
'}';
}
序列化对象
public class TestObjectOutputStream {
public static void main(String[] args) {
//创建对象输出字节流与文件输出字节流对象
try(ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("d:/data3"))){
//创建Users对象
Users users = new Users(01,"jia","18");
//将对象序列化到文件中
oos.writeObject(users);
//刷新
oos.flush();
}catch(IOException e){
e.printStackTrace();
}
}
}
5 对象反序列化到内存中
(强转类型转换成对象类型——>注意异常ClassNotFoundException)
public class TestObjectInputStream {
public static void main(String[] args) {
//创建对象输入字节流与文件字节输入流对象
try(ObjectInputStream ois = new ObjectInputStream(new FileInputStream("d:/data3"))){
//将对象反序列化到内存中
Users users = (Users) ois.readObject();
System.out.println(users);
}catch(Exception e){
e.printStackTrace();
}
}
}
三 (扩展)
1 File类在IO中的作用
(在Java中万物皆对象,所以操作文件或者目录时,需要转换成对象的)
(!!!节点流里面的文件路径可以换成File类里面在给定路径——>有什么用——>如果用IO流之前,需要对文件有操作行为,之后就可以直接用这个File类对象了!!!)
(如果仅仅是在IO流对文件操作路径就没必要用File类了)
public class TestFile {
public static void main(String[] args) {
//创建字符缓冲流与文件字符输入流对象
try(BufferedReader br = new BufferedReader(new FileReader(new File("d:/sxt.txt")));
//创建字符输出流对象
PrintWriter pw = new PrintWriter(new File("d:/sxt8.txt"))){
//操作流
String temp = "";
int i=1;
while((temp = br.readLine()) != null){
pw.println(i+","+temp);
i++;
}
pw.flush();
}catch(IOException e){
e.printStackTrace();
}
}
}
2 装饰器模式构建IO流体系
(装饰器模式处理节点流——>更清楚理解节点流和处理流关系)
1 装饰器模式简介
装饰器模式是GOF23种设计模式中较为常用的一种模式。它可以实现对原有类的包装和装饰,使新的类具有更强的功能。
2 装饰器模式
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;
}
// 功能更强的方法
public void show() {
phone.show();
System.out.println("还可以投影,在墙壁上显示");
}
}
public class TestDecoration {
public static void main(String[] args) {
Iphone phone = new Iphone("iphone30");
phone.show();
System.out.println("===============装饰后");
TouyingPhone typhone = new TouyingPhone(phone);
typhone.show();
}
}
3 IO流体系中的装饰器模式
IO流体系中大量使用了装饰器模式,让流具有更强的功能、更强的灵活性。比如:
FileInputStream fis = new FileInputStream(src);
BufferedInputStream bis = new BufferedInputStream(fis);
显然BufferedInputStream装饰了原有的FileInputStream,让普通的FileInputStream也具备了缓存功能,提高了效率。
3 第三方工具包的使用
(Apache commons-io工具包——>处理IO相关操作)
(提供了FileUtils/IOUtils——>提供更加简单功能更加强大的——>文件操作和IO流操作)
1 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等。
2 commons-io工具包
Apache的commons-io工具包中提供了IOUtils/FileUtils,为我们提供了更加简单、功能更加强大的文件操作和IO流操作功能。非常值得大家学习和使用。
3 下载与添加commons-io包
下载地址
https://commons.apache.org/proper/commons-io/download_io.cgi
添加jar包
4 FileUtils类中常用方法
(!!!方法都是静态的,直接类名.方法名就可以了!!!)
(可以和File类一起用)
(copyFileToDirectory()的第三个参数是文件过滤器——>new一个接口(本来接口不能new的)——>匿名内部类)
打开FileUtils的api文档,我们抽出一些工作中比较常用的方法,进行总结和讲解。总结如下:
方法名 | 使用说明 |
---|---|
cleanDirectory | 清空目录,但不删除目录 |
contentEquals | 比较两个文件的内容是否相同 |
copyDirectory | 将一个目录内容拷贝到另一个目录。可以通过FileFilter过滤需要拷贝的文件 |
copyFile | 将一个文件拷贝到一个新的地址 |
copyFileToDirectory | 将一个文件拷贝到某个目录下 |
copyInputStreamToFile | 将一个输入流中的内容拷贝到某个文件 |
deleteDirectory | 删除目录 |
deleteQuietly | 删除文件 |
listFiles | 列出指定目录下的所有文件 |
openInputSteam | 打开指定文件的输入流 |
readFileToString | 将文件内容作为字符串返回 |
readLines | 将文件内容按行返回到一个字符串数组中 |
size | 返回文件或目录的大小 |
write | 将字符串内容直接写到文件中 |
writeByteArrayToFile | 将字节数组内容写到文件中 |
writeLines | 将容器中的元素的toString方法返回的内容依次写入文件中 |
writeStringToFile | 将字符串内容写到文件中 |
1 读取文件内容,并输出到控制台上(之前都要嵌套——>现在只需一行代码!)
import java.io.File;
import org.apache.commons.io.FileUtils;
public class TestUtils1 {
public static void main(String[] args) throws Exception {
String content = FileUtils.readFileToString(new File("d:/a.txt"), "gbk");
System.out.println(content);
}
}
2 使用FileUtils工具类实现目录拷贝——>copyFileToDirectory()的第三个参数是文件过滤器,n
我们可以使用FileUtils完成目录拷贝,在拷贝过程中可以通过文件过滤器(FileFilter)选择拷贝内容。
import java.io.File;
import java.io.FileFilter;
import org.apache.commons.io.FileUtils;
public class TestFileUtilsDemo2 {
public static void main(String[] args) throws Exception {
FileUtils.copyDirectory(new File("d:/aaa"), new File("d:/bbb"), new FileFilter() {
@Override
public boolean accept(File pathname) {
// 使用FileFilter过滤目录和以html结尾的文件
if (pathname.isDirectory() || pathname.getName().endsWith("html")) {
return true;
} else {
return false;
}
}
});
}
}
5 IOUtils类中的常用方法
(方法大部分都是重载)
(比如说toString——>代替了(字符缓冲流—>转换流—>字节流))
打开IOUtils的api文档,我们发现它的方法大部分都是重载的。所以,我们理解它的方法并不是难事。因此,对于方法的用法总结如下:
方法名 | 使用说明 |
---|---|
buffer | 将传入的流进行包装,变成缓冲流。并可以通过参数指定缓冲大小 |
closeQueitly | 关闭流 |
contentEquals | 比较两个流中的内容是否一致 |
copy | 将输入流中的内容拷贝到输出流中,并可以指定字符编码 |
copyLarge | 将输入流中的内容拷贝到输出流中,适合大于2G内容的拷贝 |
lineIterator | 返回可以迭代每一行内容的迭代器 |
read | 将输入流中的部分内容读入到字节数组中 |
readFully | 将输入流中的所有内容读入到字节数组中 |
readLine | 读入输入流内容中的一行 |
toBufferedInputStream,toBufferedReader | 将输入转为带缓存的输入流 |
toByteArray,toCharArray | 将输入流的内容转为字节数组、字符数组 |
toString | 将输入流或数组中的内容转化为字符串 |
write | 向流里面写入内容 |
writeLine | 向流里面写入一行内容 |
我们没有必要对每个方法做测试,只是演示一下读入d:/sxt.txt文件内容到程序中,并转成String对象,打印出来。
IOUtils的使用
import java.io.*;
import org.apache.commons.io.IOUtils;
public class TestIOUtilsDemo {
public static void main(String[] args) throws Exception {
String content = IOUtils.toString(new FileInputStream("d:/sxt.txt"),"utf-8");
System.out.println(content);
}
}
6 IO流总结
-
按流的方向分类:
- 输入流:数据源到程序(InputStream、Reader读进来)。
- 输出流:程序到目的地(OutputStream、Writer写出去)。
-
按流的处理数据单元分类:
- 字节流:按照字节读取数据(InputStream、OutputStream)。
- 字符流:按照字符读取数据(Reader、Writer)。
-
按流的功能分类:
- 节点流:可以直接从数据源或目的地读写数据。
- 处理流:不直接连接到数据源或目的地,是处理流的流。通过对其他流的处理提高程序的性能。
-
(4个抽象类),(5个节点流),(11个处理流)
- InputStream/OutputStream和Reader/writer——>抽象类,是所有IO流类的抽象父类
- FileInputStream/FileOutputStream——>节点流
- BufferedInputStream/BufferedOutputStream——>处理流——>内部也缓存了数组(默认8192字节)——>read,write,flash方法
- FileReader/FileWriter——>节点流——>以字符为单位进行操作——>文本文件——>输出的时候可以直接操作字符串,也可以追加/覆盖(默认)
- BufferedReader/BufferedWriter——>处理流——>也增加了缓存机制(默认8192个字符)——>输入方法readLine()——>按行读取——>String类型的temp——>读完条件是不为空,输出方法newLine()——>换行
- InputStreamReader/OutputStreamWriter——>处理流——>可以给定指定编码转换——>字节流和字符流之间的交互
- PrintWriter——>节点流——>具有自动行刷新缓冲字符输出流——>可以按行写出字符串,并且可通过println();方法实现自动换行
-
DataInputStream/DataOutputStream——>处理流——>只能处理字节流——>以Java基本数据类型与字符串类型——>作为输入和输出
-
ObjectInputStream/ObjectOutputStream——>处理流——>只能处理字节流——>对象流——>数据流的升级——>多了对对象进行读写操作——>读取的顺序要与写入的顺序一致——>读取对象——>类必须实现
java.io.Serializable
接口——>序列化——>对象——>二进制数据 ,反序列化——>二进制数据——>对象——>writeObject(Object obj)方法,readObject()方法
- File类——>在Java中万物皆对象,所以操作文件或者目录时,需要转换成对象的——>节点流里面的文件路径可以换成File类里面在给定路径——>有什么用——>如果用IO流之前,需要对文件有操作行为,之后就可以直接用这个File类对象了——>如果仅仅是在IO流对文件操作路径就没必要用File类了
- 装饰器模式——>处理节点流——>更清楚理解节点流和处理流关系
- 第三方工具包Apache commons-io工具包——>处理IO相关操作——>提供了FileUtils/IOUtils——>提供更加简单功能更加强大的——>文件操作和IO流操作