目录
17.4.5 BufferedOutputStream类(重点)
17.4.6 BufferedInputStream类(重点)
17.4.16 ObjectOutputStream类(重点)
17.4.17 ObjectInputStream类(重点)
17.1 IO流的概念
IO就是Input和Output的简写,也就是输入和输出的含义。
IO流就是指读写数据时像流水一样从一端流到另外一端,因此得名为“流"。
17.2 基本分类
按照读写数据的基本单位不同,分为 字节流 和 字符流。
其中字节流主要指以字节为单位进行数据读写的流,可以读写任意类型的文件。
其中字符流主要指以字符(2个字节)为单位进行数据读写的流,只能读写文本文件。
按照读写数据的方向不同,分为 输入流 和 输出流(站在程序的角度)。
其中输入流主要指从文件中读取数据内容输入到程序中,也就是读文件。
其中输出流主要指将程序中的数据内容输出到文件中,也就是写文件。
按照流的角色不同分为节点流和处理流。
其中节点流主要指直接和输入输出源对接的流。
其中处理流主要指需要建立在节点流的基础之上的流。
17.3 体系结构
17.4 相关流的详解
17.4.1 FileWriter类(重点)
(1)基本概念
java.io.FileWriter类主要用于将文本内容写入到文本文件。
若文件不存在,该流会自动创建新的文件
(2)常用方法
(3)测试代码
package com.lagou.task16;
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
/**
* FileWriterTest 字符流 输出
* @param args
*/
public static void main(String[] args) {
FileWriter fw = null;
try {
//1.创建FileWrite对象并关联文件 d:/a.txt
//如果没有文件会自动创建新的文件,若文件存在则会清空文件的原有内容
fw = new FileWriter("d:/a.txt");
//如果没有文件会自动创建新的文件,若文件存在则会保留文件原有的内容
//fw = new FileWriter("d:/a.txt",true);
//2.通过流对象向文件中写入数据
//每写入一个字符,文件中的读写位置就会向后移动一位
fw.write("c");//c
//准备一个字符数组
char[] arr = new char[]{'y','u','x','c','b'};
fw.write(arr,2,3);//xcb
fw.write(arr);//yuxcb
System.out.println("写入数据成功...");
} catch (IOException e) {
e.printStackTrace();
}finally{
//3.关闭流
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
17.4.2 FileReader类(重点)
(1)基本概念
java.io.FileReader类主要用于从文本文件读取文本数据内容。
(2)常用的方法
(3)测试代码
package com.lagou.task16;
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader fr = null;
try {
//1.创建FileReader流的对象并关联d:/a.txt
fr = new FileReader("d:a.txt");
//2.用流读取文件中的数据
//读取单个字符并返回字符对应的ascii值
/*int res = 0;
while( (res = fr.read()) != -1){ //如果没有读取到文件的末尾,就一直读取
System.out.println("读取到的单个字符是:"+res);//将ascii强转成对应的字符
}*/
/* 错误:如果不用一个变量接收fr.read()的值,那么每次执行fr.read()后,读取数据的光标就会向后移动一位,在sout时打印的是下一个字符
while( (fr.read()) != -1){ //如果没有读取到文件的末尾,就一直读取
System.out.println("读取到的单个字符是:"+(char)(fr.read()));//将ascii强转成对应的字符
}*/
//将文件中的数据读取到数组中 文件a.txt : cxcbyuxcb
char[] arr = new char[5];
/*
//从文件a.txt中读取三个字符放入arr数组中下标位置从1开始
int ic = fr.read(arr, 1, 3);//返回的是读取到的字符个数
System.out.println("读取到的字符个数是:"+ic);
//遍历arr数组
for (char ch:arr) {
System.out.println("读取到的单个字符是:"+(char)ch);
}*/
//期望将读取的数据填满整个arr数组
int i = fr.read(arr);//返回读取单个字符的个数
System.out.println("读取到单个字符的个数是:"+i);
//遍历arr数组 cxcby
for (char ch: arr) {
System.out.println("读取到的单个字符是:"+ch);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭流
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
(4)字符流FileReader和FileWriter拷贝文件
package com.lagou.task16;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class FileCharCopyTest {
/**
* 用字符流 将d:/a.txt文件 拷贝到 d:/b.txt
* @param args
*/
public static void main(String[] args) {
FileReader fr = null;
FileWriter fw = null;
try {
//1.创建一个输入流对象关联d:/a.txt
fr = new FileReader("d:/a.txt");
//2.创建一个输出流对象关联d:/b.txt
fw = new FileWriter("d:/b.txt");
//3.输入流对象不断从d:/a.txt读取数据,用输出流对象写到d:/b.txt
int res = 0;
//每次读取单个字符
while ((res=fr.read()) != -1) {
//每次写入单个字符
fw.write(res);
}
System.out.println("拷贝文件成功!!!");
} catch (IOException e) {
e.printStackTrace();
} finally {
//关闭流并释放资源
if (fw != null) {
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fr != null) {
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
17.4.3 FileOutputStream类(重点)
(1)基本概念
java.io.FileOutputStream类主要用于将图像数据之类的原始字节流写入到输出流中。
(2)常用的方法
(3)测试代码
同17.4.4中的(3)
17.4.4 FileInputStream类(重点)
(1)基本概念
java.io.FileInputStream类主要用于从输入流中以字节流的方式读取图像数据等。
(2)常用的方法
(3)测试代码
字节流 FileInputStream和FileOutputStream拷贝文件
package com.lagou.task16;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileByteCopyTest {
/**
* 字节流实现文件的拷贝 d:/图片a.jpg 拷贝到 d:/图片b.jpg
* @param args
*/
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
//1.创建字节输入流对象并关联文件 d:/图片a.jpg
//fis = new FileInputStream("d:/图片a.jpg");
fis = new FileInputStream("d:/视频a.mp4");
//2.创建字节输出流对象并关联文件 d:/图片b.jpg
//fos = new FileOutputStream("d:/图片b.jpg");
fos = new FileOutputStream("d:/视频b.mp4");
//3.字节输入流对象不断的读取文件的数据, 并写入到输出流中
System.out.println("正在玩命拷贝文件...");
//方式一缺点:每次只能在内存中读取并写入一个字节,效率比较低
/*int res = 0;
while ((res = fis.read())!=-1){
fos.write(res);
}*/
//方式二:准备一个和文件大小一样的缓冲区,每次将文件中的所有内容读取到缓冲区并一次性写入
//方式二缺点:如果文件的内容过大,可能无法申请和文件一样大小的缓冲区
/*
//获取文件大小
int len = fis.available();
System.out.println("获取的文件大小是:"+len);
byte[] brr = new byte[len];
//输入流一次读取最多brr.length个数的字节到brr字节数组中
int res = fis.read(brr);
System.out.println("读取的字节个数是:"+res);
//将brr.length个字节从brr字节数组读取到字节输出流中
fos.write(brr);*/
//方式三:申请大小为1024整数倍的缓冲区,文件如果内容过大,可以分多次拷贝文件
byte[] brr = new byte[1024];
int res = 0;//读取到的字节个数
//每次最多读取1024个字节到数组中,然后将读取到的字节写入到输出流中,若最后一次读取的自己不足1024,则有多少读取多少
while((res = fis.read(brr))!=-1){
fos.write(brr,0,res);//将brr数组中从下标0开始,长度为res个字节写入输出流中
}
System.out.println("字节流拷贝文件成功...");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流并释放资源
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (fis != null) {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
17.4.5 BufferedOutputStream类(重点)
(1)基本概念
java.io.BufferedOutputStream类主要用于描述缓冲输出流,此时不用为写入的每个字节调用底层系统。
(2)常用的方法
(3)测试代码
同17.4.6中的(3)
17.4.6 BufferedInputStream类(重点)
(1)基本概念
java.io.BufferedInputStream类主要用于描述缓冲输入流。
(2)常用的方法
(3)测试代码
缓冲字节流BufferedInputStream和BufferedOutputStream拷贝文件
package com.lagou.task16;
import java.io.*;
public class BufferedByteCopyTest {
public static void main(String[] args) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.创建BufferedInputStream缓冲字节输入流对象并和d:/视频a.mp4文件关联
bis = new BufferedInputStream(new FileInputStream("d:/视频a.mp4"));
//2.创建BufferedOutputStream缓冲字节输出流对象并和d:/视频b.mp4文件关联
bos = new BufferedOutputStream(new FileOutputStream("d:/视频b.mp4"));
//3.从缓冲输入流不断的读取数据写入到缓冲输出流
//创建一个byte数据,用于存放读取到的数据
byte[] brr = new byte[1024];
//从bis输入流中最多将brr.length个字节的数据读取到brr数组中,返回值是实际读取到的字节个数
int res = 0;//记录从输入流中读取到的字节个数
System.out.println("正在玩命拷贝文件...");
while((res=bis.read(brr)) != -1){
//将brr数组中读取到的数据写入到输出流
bos.write(brr,0,res);
}
System.out.println("成功拷贝文件!!!");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流对象并释放资源
if (bos != null) {
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bis != null) {
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
17.4.7 BufferedWriter类(重点)
(1)基本概念
java.io.BufferedWriter类主要用于写入单个字符、字符数组以及字符串到输出流中。
(2)常用的方法
(3)测试代码
同17.4.8中的(3)
17.4.8 BufferedReader类(重点)
(1)基本概念
java.io.BufferedReader类用于从输入流中读取单个字符、字符数组以及字符串。
(2)常用的方法
(3)测试代码
缓冲字符流BufferedReader和BufferedWtiter拷贝文件
package com.lagou.task16;
import java.io.*;
public class BufferedCharCopyTest {
public static void main(String[] args) {
BufferedWriter bw = null;
BufferedReader br = null;
try {
//1.创建缓冲字符输出流对象BufferedWriter并和文件d:/a.txt关联
bw = new BufferedWriter(new FileWriter("d:/b.txt"));
//2.创建缓冲字符输入流对象BufferedReader并和文件d:/b.txt关联
br = new BufferedReader(new FileReader("d:/a.txt"));
//3.从缓冲字符流中不断的读取整行数据写入到缓冲字符输出流
String res = null;
System.out.println("正在玩命拷贝文件...");
while ((res=br.readLine()) != null){//从输入流读取整行数据
bw.write(res);//将指定的字符串写入输出流中
bw.newLine();//写入行分隔符到输出流中
}
System.out.println("成功拷贝文件!!!");
} catch (IOException e) {
e.printStackTrace();
} finally {
//4.关闭流对象并释放资源
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (bw != null) {
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
17.4.9 PrintStream类
(1)基本概念
java.io.PrintStream类主要用于更加方便地打印各种数据内容。
(2)常用的方法
17.4.10 PrintWriter类
(1)基本概念
java.io.PrintWriter类主要用于将对象的格式化形式打印到文本输出流。
(2)常用的方法
(3)测试代码
不断地提示用户输入要发送的内容,若发送的内容是"bye"则聊天结束,否则将用户输入的内容写
入到文件d:/a.txt中。
要求使用BufferedReader类(不用Scanner类)来读取键盘的输入 System.in代表键盘输入
要求使用PrintStream类负责将数据写入文件
package com.lagou.task16;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
public class PrintStreamChatTest {
public static void main(String[] args) {
//用BufferedReader字符流来接收键盘输入的内容, System.in代表键盘输入
//由手册可知:BufferedReader构造方法需要的参数是Reader 类型(抽象类) 字符流
//由手册可知:System.in代表键盘输入并属于InputStream类型 字节流
//问题:需要Reader(字符流参数),实际提供的是System.in(字节流参数)
//解决办法:利用InputStreamReader类,将字节 转换成 字符流,InputStreamReader(InputStream in)
BufferedReader br = null;
PrintWriter pw = null;
try {
//将字节流System.in 转换成 字符流Reader
br = new BufferedReader(new InputStreamReader(System.in));
pw = new PrintWriter(new FileOutputStream("d:/a.txt"));
//切换打字人身份的标志
boolean flag = true;
while(true) {
//1.提示用户要发送的聊天内容并用变量记录
System.out.println("请"+ (flag? "张三" : "李四")+"输入聊天内容:");
String s = br.readLine();//读取用户输入的内容(一行)
//2.若聊天内容是"bye",则退出该聊天
if ("bye".equals(s)) {
System.out.println("退出聊天...");
break;
//3.若聊天内容不是"bye",将聊天的内容写入到d:/a.txt文件中
}
//else {
//获取当前系统时间,并格式化
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
String formatDate = sdf.format(date);//对date做格式化处理
pw.println(formatDate+(flag? " 张三说:" : " 李四说:")+s);
//}
//切换身份
flag = !flag;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
}
//4.关闭流并释放资源
if (pw != null) {
pw.close();
}
if (br != null) {
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
17.4.11 OutputStreamWriter类
(1)基本概念
java.io.OutputStreamWriter类主要用于实现从字符流到字节流的转换。
(2)常用的方法
17.4.12 InputStreamReader类
(1)基本概念
java.io.InputStreamReader类主要用于实现从字节流到字符流的转换。
(2)常用的方法
17.4.13 字符编码
(1)编码表的由来
计算机只能识别二进制数据,早期就是电信号。为了方便计算机可以识别各个国家的文字,就需要
将各个国家的文字采用数字编号的方式进行描述并建立对应的关系表,该表就叫做编码表。
(2)常见的编码表
ASCII:美国标准信息交换码, 使用一个字节的低7位二位进制进行表示。
ISO8859-1:拉丁码表,欧洲码表,使用一个字节的8位二进制进行表示。
GB2312:中国的中文编码表,最多使用两个字节16位二进制为进行表示。
GBK:中国的中文编码表升级,融合了更多的中文文字符号,最多使用两个字节16位二进制位表示。
Unicode:国际标准码,融合了目前人类使用的所有字符,为每个字符分配唯一的字符码。所有的文字都用两个字节16位二进制位来表示。
(3)编码的发展
a. 面向传输的众多 UTF(UCS Transfer Format)标准出现了,UTF-8就是每次8个位传输数据,而UTF-16就是每次16个位。
这是为传输而设计的编码并使编码无国界,这样就可以显示全世界上所有文化的字符了。
b. Unicode只是定义了一个庞大的、全球通用的字符集,并为每个字符规定了唯一确定的编号,具体存储成什么样的字节流,
取决于字符编码方案。推荐的Unicode编码是UTF-8和UTF-16。
c. UTF-8:变长的编码方式,可用1-4个字节来表示一个字符。
17.4.14 DataOutputStream类(了解)
(1)基本概念
java.io.DataOutputStream类主要用于以适当的方式将基本数据类型写入输出流中。
(2)常用的方法
(3)测试代码
package com.lagou.task16;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamTest {
public static void main(String[] args) {
ObjectOutputStream oot = null;
try {
//1..创建ObjectOutputStream类输出流对象并和d:/b.txt文件关联
oot = new ObjectOutputStream(new FileOutputStream("d:/b.txt"));
//2.创建一个user对象
User user = new User("张飞", "qwer", "13671068896");
//3.将user对象写入输出流
oot.writeObject(user);
System.out.println("对象写入成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
}
//4.关闭流并释放资源
if (oot != null) {
try {
oot.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
17.4.15 DataInputStream类(了解)
(1)基本概念
java.io.DataInputStream类主要用于从输入流中读取基本数据类型的数据。
(2)常用的方法
(3)测试代码
package com.lagou.task16;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ObjectInputStreamTest {
public static void main(String[] args) {
ObjectInputStream ois = null;
try {
//1.创建ObjectInputStream类对象并和d:/b.txt文件关联
ois = new ObjectInputStream(new FileInputStream("d:/b.txt"));
//2.从输入流中读取对象 并打印
Object obj = ois.readObject();
System.out.println(obj);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
//3.关闭流并释放资源
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
17.4.16 ObjectOutputStream类(重点)
(1)基本概念
java.io.ObjectOutputStream类主要用于将一个对象的所有内容整体写入到输出流中,保存在文件中。
只能将支持 java.io.Serializable 接口的对象写入流中。
类通过实现 java.io.Serializable 接口以启用其序列化功能。
所谓序列化主要指将一个对象需要存储的相关信息有效组织成字节序列的转化过程。
(2)常用的方法
(3)测试对象User类
package com.lagou.task16;
import java.io.Serializable;
public class User implements Serializable {
//实现Serializable接口,需要声明一个序列化版本号
private static final long serialVersionUID = 6030616519770130833L;
private String userName; //用户名
private String password;//密码
private transient String phoneNum; //手机号 transient不参与序列化
public User(String userName, String password, String phoneNum) {
this.userName = userName;
this.password = password;
this.phoneNum = phoneNum;
}
public User() {
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
@Override
public String toString() {
return "User{" +
"userName='" + userName + '\'' +
", password=" + password +
", phoneNum=" + phoneNum +
'}';
}
}
(4)测试代码
package com.lagou.task16;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
public class ObjectOutputStreamTest {
public static void main(String[] args) {
ObjectOutputStream oot = null;
try {
//1..创建ObjectOutputStream类输出流对象并和d:/b.txt文件关联
oot = new ObjectOutputStream(new FileOutputStream("d:/b.txt"));
//2.创建一个user对象
User user = new User("张飞", "qwer", "13671068896");
//3.将user对象写入输出流
oot.writeObject(user);
System.out.println("对象写入成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
}
//4.关闭流并释放资源
if (oot != null) {
try {
oot.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
17.4.17 ObjectInputStream类(重点)
(1)基本概念
java.io.ObjectInputStream类主要用于从输入流中一次性将对象整体读取出来。
所谓反序列化主要指将有效组织的字节序列恢复为一个对象及相关信息的转化过程。
(2)常用的方法
(3)序列化版本号
序列化机制是通过在运行时判断类的serialVersionUID来验证版本一致性的。在进行反序列化时,
JVM会把传来的字节流中的serialVersionUID与本地相应实体类的serialVersionUID进行比较,如
果相同就认为是一致的,可以进行反序列化,否则就会出现序列化版本不一致的异常(InvalidCastException)。
(4)transient关键字
transient是Java语言的关键字,用来表示一个域不是该对象串行化的一部分。当一个对象被串行化的
时候,transient型变量的值不包括在串行化的表示中,然而非transient型的变量是被包括进去的。
(5)经验的分享
当希望将多个对象写入文件时,通常建议将多个对象放入一个集合中,然后将集合这个整体看做一
个对象写入输出流中,此时只需要调用一次readObject方法就可以将整个集合的数据读取出来,
从而避免了通过返回值进行是否达到文件末尾的判断。
(6)测试对象
同17.4.16中的(3)
(7)测试代码
package com.lagou.task16;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
public class ObjectInputStreamTest {
public static void main(String[] args) {
ObjectInputStream ois = null;
try {
//1.创建ObjectInputStream类对象并和d:/b.txt文件关联
ois = new ObjectInputStream(new FileInputStream("d:/b.txt"));
//2.从输入流中读取对象 并打印
Object obj = ois.readObject();
System.out.println(obj);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
//3.关闭流并释放资源
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
17.4.18 RandomAccessFile类
(1)基本概念
java.io.RandomAccessFile类主要支持对随机访问文件的读写操作。
(2)常用的方法
(3)测试代码
package com.lagou.task16;
import java.io.IOException;
import java.io.RandomAccessFile;
public class RandomAccessFileTest {
public static void main(String[] args) {
RandomAccessFile raf = null;
try {
//1.创建RandomAccessFile类对象并和d:/a.txt关联 aellhello
raf = new RandomAccessFile("d:/a.txt", "rw");
//2.对数据进行读写操作
//对读取位置设置偏移量
raf.seek(4); //h e
int ia = raf.read();
System.out.println("读取到的字符是:"+(char)ia);//h
ia = raf.read();
System.out.println("读取到的字符是:"+(char)ia);//e
//从偏移量的位置写入字符
raf.write('2'); //aellhello => aellhe2lo 改段代码执行覆盖了第三个'l'
System.out.println("写入数据成功...");
} catch (IOException e) {
e.printStackTrace();
} finally {
}
//3.关闭流
if (raf != null) {
try {
raf.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}