字节缓冲流
构造方法
-
字节缓冲流介绍
- BufferedOutputStream:该类实现缓冲输出流.通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用
- BufferedInputStream:创建BufferedInputStream将创建一个内部缓冲区数组.当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节
-
构造方法:
方法名 说明 BufferedOutputStream(OutputStream out) 创建字节缓冲输出流对象 BufferedInputStream(InputStream in) 创建字节缓冲输入流对象 -
读写和关闭流的方法 同字节输入输出流
代码
- 用BufferedOutputStream写数据到文件
- 用BufferedInputStream把刚才写入的文件读出来
//缓冲字节输出流对象 创建
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("module01/aaa.txt"));
//写入
bos.write(99);
bos.write(100);
bos.write(101);
//关闭
bos.close();
//缓冲字节输入流对象 创建 用来读取aaa.txt
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("module01/aaa.txt"));
//循环读取aaa.txt
int b;
while ((b = bis.read()) != -1) {
System.out.println((char)b);
}
//关闭流
bis.close();
缓冲流原理
-
减少了对硬盘的操作,写入数据到数组
-
1 数组满了之后,才会自动写入到硬盘
-
2 主动调用flush后 也会写入到硬盘 注意close方法中 已经调用了flush
-
每次从数组里读,这样操作的是内存,效率高,直到把数组内容读完,数组在去硬盘获取
flush方法
- 调用flush方法,可以主动把数据写入到硬盘
- close方法里也调用了flush方法
字节缓冲流复制一个视频练习
一次一个字节代码
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("module01/music/无间道.mp3"));
//缓冲字节输出流对象 创建
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("module01/music/无间道1.mp3"));
int b;
while ((b = bis.read()) != -1) {
//把读取的内容写入到无间道1.mp3
bos.write(b);
}
bos.close();
bis.close();
}
一次一个数组代码
public static void main(String[] args) throws IOException {
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("module01/music/无间道.mp3"));
//缓冲字节输出流对象 创建
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("module01/music/无间道1.mp3"));
byte[] b = new byte[1024];
int len;
while ((len = bis.read(b)) != -1) {
//把读取的内容写入到无间道1.mp3
bos.write(b, 0, len);
}
bos.close();
bis.close();
}
字符流
介绍
- 由于字节流操作中文不是特别的方便,所以Java就提供字符流
- 字符流 = 字节流 + 编码表
- 中文的字节存储方式
- 用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
- 汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
编码表(了解)
-
什么是字符集
是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
l计算机要准确的存储和识别各种字符集符号,就需要进行字符编码,一套字符集必然至少有一套字符编码。常见字符集有ASCII字符集、GBXXX字符集、Unicode字符集等
-
常见的字符集
-
ASCII字符集:
lASCII:是基于拉丁字母的一套电脑编码系统,用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显示字符(英文大小写字符、阿拉伯数字和西文符号)
基本的ASCII字符集,使用7位表示一个字符,共128字符。ASCII的扩展字符集使用8位表示一个字符,共256字符,方便支持欧洲常用字符。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等
-
GBXXX字符集:
GBK:最常用的中文码表。是在GB2312标准基础上的扩展规范,使用了双字节编码方案,共收录了21003个汉字,完全兼容GB2312标准,同时支持繁体汉字以及日韩汉字等
-
Unicode字符集:
UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用 中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
-
编码规则:
- 128个US-ASCII字符,只需一个字节编码
- 拉丁文等字符,需要二个字节编码
- 大部分常用字(含中文),使用三个字节编码
- 其他极少使用的Unicode辅助字符,使用四字节编码
字符串中的编码解码问题(会使用)
-
相关方法
方法名 说明 byte[] getBytes() 使用平台的默认字符集将该 String编码为一系列字节 byte[] getBytes(String charsetName) 使用指定的字符集将该 String编码为一系列字节 String(byte[] bytes) 使用平台的默认字符集解码指定的字节数组来创建字符串 String(byte[] bytes, String charsetName) 通过指定的字符集解码指定的字节数组来创建字符串
代码
- encode编码 decode解码
String str01 = "我爱我的祖国";
byte[] bytes = str01.getBytes("utf-8");
System.out.println(Arrays.toString(bytes));
byte[] bytes1 = str01.getBytes("gbk");
System.out.println(Arrays.toString(bytes1));
String str02 = new String(bytes,"utf-8");
System.out.println(str02);
String str03 = new String(bytes1,"gbk");
System.out.println(str03);
idea设置编码
字符流类
-
Writer和Reader
-
字节流 操作一切文件
-
字符流 操作文本文件 操作汉字时更方便
FileWriter
- Writer: 用于写入字符流的抽象父类
- FileWriter: 用于写入字符流的常用子类
构造方法
方法名 | 说明 |
---|---|
FileWriter(File file) | 根据给定的 File 对象构造一个 FileWriter 对象 |
FileWriter(File file, boolean append) | 根据给定的 File 对象构造一个 FileWriter 对象 |
FileWriter(String fileName) | 根据给定的文件名构造一个 FileWriter 对象 |
FileWriter(String fileName, boolean append) | 根据给定的文件名以及指示是否附加写入数据的 boolean 值来构造 FileWriter 对象 |
jdk11后构造方法可以指定编码
public static void main(String[] args) throws IOException {
//jdk11后 支持指定编码解码方式
//参数2指定编码为utf-8 注意语法是Charset.forName()
FileWriter fw = new FileWriter("qqqqqq.txt", Charset.forName("utf-8"));
fw.write("开心的文字 拆迁啦");
fw.close();
//参数2 指定解码方式为utf-8
FileReader fr = new FileReader("qqqqqq.txt", Charset.forName("utf-8"));
char[] cs = new char[1000];
int len = fr.read(cs);
fr.close();
System.out.println(new String(cs, 0, len));
}
成员方法
方法名 | 说明 |
---|---|
void write(int c) | 写一个字符 |
void write(char[] cbuf) | 写入一个字符数组 |
void write(char[] cbuf, int off, int len) | 写入字符数组的一部分 |
void write(String str) | 写一个字符串 |
void write(String str, int off, int len) | 写一个字符串的一部分 |
刷新和关闭的方法
方法名 | 说明 |
---|---|
flush() | 刷新流,之后还可以继续写数据 |
close() | 关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据 |
代码
//FileWriter fw = new FileWriter(new File("module01/ccc.txt"));
FileWriter fw = new FileWriter("module01/ccc.txt");
fw.write(100);
char[] cs = {'a', 'g', '啊', '吃'};
fw.write(cs);
fw.write("饭了,吃烤鸭,吃宫爆鸡丁,吃鱼香肉丝");
fw.write("\r\n"); //写入换行
fw.write("abcdefg", 1, 3);//跳过1个写入3个
fw.close();
FileWrite本身就有缓冲区
- 如果write少量数据,没有flush也没有close,会发现没有写入文件
- close方法执行后,不能继续使用流对象了
FileReader
- Reader: 用于读取字符流的抽象父类
- FileReader: 用于读取字符流的常用子类
构造方法
方法名 | 说明 |
---|---|
FileReader(File file) | 在给定从中读取数据的 File 的情况下创建一个新 FileReader |
FileReader(String fileName) | 在给定从中读取数据的文件名的情况下创建一个新 FileReader |
成员方法
方法名 | 说明 |
---|---|
int read() | 一次读一个字符数据 |
int read(char[] cbuf) | 一次读一个字符数组数据 |
代码演示
//FileReader fr = new FileReader(new File("module01/ccc.txt"));
FileReader fr = new FileReader("module01/ccc.txt");
//int a;
//while ((a = fr.read()) != -1) {
// System.out.println(a);
// System.out.println((char) a);
//}
//System.out.println("\r\n".toCharArray().length);
char[] cs = new char[3];
int len;
while ((len = fr.read(cs)) != -1) {
//System.out.println(cs);
System.out.println(new String(cs, 0, len));//转为字符串 注意要添加len长度,最后一次遍历数组可能装不满
}
fr.close();
字符流用户注册案例
-
案例需求
将键盘录入的用户名和密码保存到本地实现永久化存储
-
实现步骤
- 1获取用户输入的用户名和密码
- 2将用户输入的用户名和密码写入到本地文件中
- 3关流,释放资源
代码
//- 1获取用户输入的用户名和密码
//- 2将用户输入的用户名和密码写入到本地文件中
//- 3关流,释放资源
Scanner sc = new Scanner(System.in);
System.out.println("输入的用户名:");
String name = sc.next();
System.out.println("输入的密码:");
String pwd = sc.next();
try(FileWriter fw = new FileWriter("user.txt");) {
//写入用户名
fw.write(name);
//换行
fw.write("\r\n");
//写入密码
fw.write(pwd);
} catch (IOException e) {
e.printStackTrace();
}
FileOutputStream
BufferedOutputStream
FileWrite 字符流本身就有一个缓冲数组 8kb
BufferedWrite
字符缓冲流
-
介绍
- BufferedWriter:将文本写入字符输出流,缓冲字符,以提供单个字符,数组和字符串的高效写入,可以指定缓冲区大小,或者可以接受默认大小。默认值足够大,可用于大多数用途
- BufferedReader:从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取,可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途
-
构造方法
方法名 说明 BufferedWriter(Writer out) 创建字符缓冲输出流对象 BufferedReader(Reader in) 创建字符缓冲输入流对象
代码
- BufferedWriter写数据
- BufferedReader读数据
try (BufferedReader br = new BufferedReader(new FileReader("mymodule/d.txt"));
BufferedWriter bw = new BufferedWriter(new FileWriter("mymodule/d1.txt"));) {
//定义字符数组
char[] cs = new char[1024];
//定义长度
int len = 0;
while ((len = br.read(cs)) != -1) {
bw.write(cs, 0, len);
}
} catch (IOException e) {
e.printStackTrace();
}
字符缓冲流特有功能
-
方法介绍
BufferedWriter:
方法名 说明 void newLine() 写一行行分隔符,行分隔符字符串由系统属性定义 BufferedReader:
方法名 说明 String readLine() 读一行文字。 结果包含行的内容的字符串,不包括任何行终止字符如果流的结尾已经到达,则为null
代码
- 1循环写入10次hello world到文件,每次都换行
- 2按行读取文件内容
// - 1循环写入10次hello world到文件,每次都换行
//创建缓冲字符写入流
try(bw = new BufferedWriter(new FileWriter("module01/xxx.txt"));){
for (int i = 0; i < 10; i++) {
bw.write("hello world");
//换行
bw.newLine();
}
} catch (IOException e) {
e.printStackTrace();
}
// - 2按行读取文件内容
try( //创建缓冲字符读取流
BufferedReader br = new BufferedReader(new FileReader("module01/xxx.txt"));) {
//定义字符串接收读取的内容
String line;
//每次读取一行 内容全部读完后,如果再读 就会返回null 把循环结束
while ((line = br.readLine()) != null) {
System.out.println(line);
}
} catch (IOException e) {
e.printStackTrace();
}
字符缓冲流操作文件中数据排序案例
需求
- 使用字符缓冲流读取文件中的数据,排序后再次写到本地文件
- 文件内容是
5 32 47 7 5 22 2 12 4 8 6
步骤
- 1将文件中的数据读取到程序中
- 2对读取到的数据进行分割转换排序
- 3将排序后的数据写入到文件中
代码
//1 读取文件内容 按行读取
try (
BufferedReader br = new BufferedReader(new FileReader("mymodule/ee.txt"));
) {
String line = br.readLine();//读取数据5 32 47 7 5 22 2 12 4 8 6
//2 把读取的字符串 按空格切割 得到一个字符串数组
String[] ss = line.split(" ");
System.out.println(Arrays.toString(ss));
//把字符串的数组转为int数组
int[] arr = new int[ss.length];
for (int i = 0; i < ss.length; i++) {
arr[i] = Integer.parseInt(ss[i]);
}
//3 对字符串数组排序
Arrays.sort(arr);
System.out.println(Arrays.toString(arr));
//4 把排序完的数据写回文件
BufferedWriter bw = new BufferedWriter(new FileWriter("mymodule/ee.txt"));
for (int i : arr) {
//遍历写回内容 注意加空格
bw.write(i + " ");
}
//关闭流
bw.close();
} catch (IOException e) {
}
#转换流(理解)
两个类(可以在读写时,指定编码集)
- InputStreamReader
- OutputStreamWriter
jdk11后使用
- FileWriter 和FileReader
构造方法
方法名 | 说明 |
---|---|
InputStreamReader(InputStream in) | 使用默认字符编码创建InputStreamReader对象 |
InputStreamReader(InputStream in,String chatset) | 使用指定的字符编码创建InputStreamReader对象 |
OutputStreamWriter(OutputStream out) | 使用默认字符编码创建OutputStreamWriter对象 |
OutputStreamWriter(OutputStream out,String charset) | 使用指定的字符编码创建OutputStreamWriter对象 |
代码演示
- 用OutputStreamWriter 写入汉字 指定编码
- 用InputStreamReader 读取汉字 指定编码
public class ConversionStreamDemo {
public static void main(String[] args) throws IOException {
//创建写入的转换流对象 使用默认编码
//OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"));
//创建写入的转换流对象 指定编码为gbk
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("myCharStream\\osw.txt"),"GBK");
osw.write("中国");
osw.close();
//创建读取的转换流对象 使用默认编码
//InputStreamReader isr = new InputStreamReader(new FileInputStream("myCharStream\\osw.txt"));
//创建读取的转换流对象 指定编码为gbk
InputStreamReader isr = new InputStreamReader(new FileInputStream("myCharStream\\osw.txt"),"GBK");
//一次读取一个字符数据
int ch;
while ((ch=isr.read())!=-1) {
System.out.print((char)ch);
}
isr.close();
}
}
注意:jdk11后用文件读写流就可以指定编码了
public static void main(String[] args) throws IOException {
//jdk11后 支持指定编码解码方式
//参数2指定编码为utf-8 注意语法是Charset.forName()
FileWriter fw = new FileWriter("qqqqqq.txt", Charset.forName("utf-8"));
fw.write("开心的文字 拆迁啦");
fw.close();
//参数2 指定解码方式为utf-8
FileReader fr = new FileReader("qqqqqq.txt", Charset.forName("utf-8"));
char[] cs = new char[1000];
//读取数据到数组
int len = fr.read(cs);
//关闭流
fr.close();
System.out.println(new String(cs, 0, len));
}
对象操作流
Serializable 可序列化的 形容词
Serializer 序列化器 名词
Serialize 序列化 动词
对象 -- 》 字节序列 就可以保存 在文件里 也可以在网络中传输
新闻
class News Implements Serializable{
String tilte;
Strint content;
Date date;
}
News n = new News();
//创建对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("news.txt"));
//把对象写入
oos.writeObject(n);
//关闭流
oos.close();
javasscript
前端收到的是新闻的字节数据
把字节数据 转回成对象 反序列化
对象序列化流
-
对象序列化介绍
- 对象序列化:把对象转换为可以存储或传输的形式的过程
- 这种机制就是使用一个字节序列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息
- 字节序列写到文件之后,相当于文件中持久保存了一个对象的信息。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化
-
对象序列化流: ObjectOutputStream
- 将Java对象的原始数据类型和图形写入OutputStream。 可以使用ObjectInputStream读取(重构)对象。 可以通过使用流的文件来实现对象的持久存储。 如果流是网络套接字流,则可以在另一个主机上或另一个进程中重构对象
-
构造方法
方法名 说明 ObjectOutputStream(OutputStream out) 创建一个写入指定的OutputStream的ObjectOutputStream -
序列化对象的方法
方法名 说明 void writeObject(Object obj) 将指定的对象写入ObjectOutputStream
代码
- 动物类
public class Anima implements Serializable {
public String name;
protected int age;
public Anima() {
}
public Anima(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Anima{" +
"name='" + name + '\'' +
", age=" + age;
}
}
测试类
public class Demo01 {
public static void main(String[] args) throws IOException {
//创建对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("animal.txt"));
//Anima类要实现Serializable接口
Anima a = new Anima("大象", 3);
//把对象写入
oos.writeObject(a);
//关闭流
oos.close();
}
}
注意事项
- 一个对象要想被序列化,该对象所属的类必须必须实现Serializable 接口
- Serializable是一个标记接口,实现该接口,不需要重写任何方法
对象反序列化流
-
对象反序列化流: ObjectInputStream
- ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
-
构造方法
方法名 说明 ObjectInputStream(InputStream in) 创建从指定的InputStream读取的ObjectInputStream -
反序列化对象的方法
方法名 说明 Object readObject() 从ObjectInputStream读取一个对象
代码
//创建对象输出流
ObjectInputStream oos = new ObjectInputStream(new FileInputStream("animal.txt"));
//读取出对象 然后强转为Anima对象 这个过程也叫反序列化
Anima a = (Anima) oos.readObject();
oos.close();
//打印读取的对象
System.out.println(a);
serialVersionUID&transient
- serialVersionUID
- 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
- 会出问题,会抛出InvalidClassException异常
- 用对象序列化流序列化了一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
Exception in thread "main" java.io.InvalidClassException: com.heima1.test3.Anima; local class incompatible: stream classdesc serialVersionUID = 4726774650000051461, local class serialVersionUID = -3497608134273938572
-
如果出问题了,如何解决呢?
-
1给对象所属的类加一个serialVersionUID
- private static final long serialVersionUID = 42L;
-
2重新序列化,再次测试
-
transient
- 如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
- 给该成员变量加transient关键字修饰,该关键字标记的成员变量不参与序列化过程
代码
- 学生类
public class Anima implements Serializable {
public static final long serialVersionUID = 42L;
public String name;
public int age;
//transient表示在序列化的时候,忽略当前的字段
public transient int weight;
public Anima(String name, int age, int weight) {
this.name = name;
this.age = age;
this.weight = weight;
}
public Anima() {
}
@Override
public String toString() {
return "Anima{" +
"name='" + name + '\'' +
", age=" + age +
", weight=" + weight +
'}';
}
- 序列化测试
- Anima对象 有一个400斤的体重,然后执行writeObject保存
//创建对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("xiongda.txt"));
//Anima类要实现Serializable接口
Anima a = new Anima("熊大", 7, 400);
//把对象写入
oos.writeObject(a);
oos.close();
- 反序列化测试
- 读取对象后打印,发现体重为0.0
- 应为weight被transient修饰,数据没有被保存
//创建对象输出流
ObjectInputStream oos = new ObjectInputStream(new FileInputStream("xiongda.txt"));
//读取出对象 然后强转为Anima对象 这个过程也叫反序列化
Anima a = (Anima) oos.readObject();
oos.close();
//打印读取的对象
System.out.println(a);//Anima{name='熊大', age=7, weight=0}
对象操作流练习
案例需求
- 创建多个学生类对象写到文件中,再次读取到内存中
步骤
- 1创建序列化流对象
- 2创建多个学生对象
- 3将学生对象添加到集合中
- 4将集合对象序列化到文件中
- 5创建反序列化流对象
- 6将文件中的对象数据,读取到内存中
代码实现
- 学生类
public class Student implements Serializable{
private static final long serialVersionUID = 2L;
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
- 测试类
public class Demo01 {
public static void main(String[] args) {
//创建多个学生类对象写到文件中,再次读取到内存中
ObjectOutputStream oos = null;
ObjectInputStream ois = null;
try {
//- 1创建序列化流对象
oos = new ObjectOutputStream(new FileOutputStream("module01/stu.data"));
//- 2创建多个学生对象
Student stu01 = new Student("xiaoming", 20);
Student stu02 = new Student("xiaohong", 21);
Student stu03 = new Student("pgo", 30);
//- 3将学生对象添加到集合中
ArrayList<Student> stus = new ArrayList<>();
stus.add(stu01);
stus.add(stu02);
stus.add(stu03);
//- 4将集合对象序列化到文件中
oos.writeObject(stus);
//- 5创建反序列化流对象
ois = new ObjectInputStream(new FileInputStream("module01/stu.data"));
//- 6将文件中的对象数据,读取到内存中
ArrayList<Student> ss = (ArrayList<Student>) ois.readObject();
System.out.println(ss);
} catch (IOException e) {
e.printStackTrace();
System.out.println("读写失败");
} catch (ClassNotFoundException e) {
e.printStackTrace();
System.out.println("找不到class");
} finally {
if (oos != null) {
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (ois != null) {
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
小结
字节流
FileOutputStream FileInputStream read write
BufferedOutputStream BufferedInputStream
字符流
FileReader FileWriter
BufferedReader readLine BufferedWriter newLine
转换流
InputStreamReader OutputStreamWriter
对象流
ObjectInputStream ObjectOutputStream