1.缓冲流概述
package com.itheima._01缓冲流概述;
/**
目标:理解缓冲流的原理
讲解:
1. 缓冲流的分类
缓冲流称为高效流
字节缓冲输入流:BufferedInputStream FileInputStream
字节缓冲输出流:BufferedOutputStream FileOutputStream
字符缓冲输入流:BufferedReader FileReader
字符缓冲输出流:BufferedWriter FileWriter
2. 缓冲流的原理
内部会准备一个缓冲区数组,临时存储数据,当缓冲区数组满了或调用flush或调用
了close方法,一次性调用底层资源将缓冲区数组数据输出到目标文件中:
减少底层资源的调用次数从而提高读写效率。
小结:
缓冲流的原理:
内部会准备一个缓冲区数组,临时存储数据,当缓冲区数组满了或调用flush或调用
了close方法,一次性调用底层资源将缓冲区数组数据输出到目标文件中:
减少底层资源的调用次数从而提高读写效率。
*/
public class Demo01 {
}
2.字节缓冲流基本使用
package com.itheima._02字节缓冲流基本使用;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
/**
目标:理解BufferedOutputStream输出数据的过程
讲解:
1. BufferedOutputStream类构造方法源码分析
在构造方法中创建了一个长度为8192的字节数组:缓冲区数组
2. BufferedOutputStream类write方法源码分析
先将数据保存内部的缓冲区数组中,
当缓冲区数据满了则会将数组的内容输出到目标文件中
3. BufferedOutputStream类close方法源码分析
close方法内部会先调用flush方法刷新缓冲区,然后在关闭流释放资源。
小结:
BufferedOutputStream输出数据的过程小结
* 先调用BufferedOutputStream的write方法将数据输出到缓冲区数组中
* 当缓冲区数组满了或调用close方法或调用flush方法,会调用FileOutputStream
的write方法将缓冲区数组的数据一次性输出到目标文件中。
*/
public class Demo022 {
public static void main(String[] args) throws Exception {
// 1. 创建字节输出流对象并关联目标文件:FileOutputStream
FileOutputStream fos = new FileOutputStream("a.txt");
// 2. 根据字节输出流对象创建字节缓冲输出流:BufferedOutputStream
BufferedOutputStream bos = new BufferedOutputStream(fos);
// 3. 调用字节缓冲输出流对象的write方法输出数据:一个字节、字节数组
bos.write(97);
bos.write("你好".getBytes());
// 4. 调用字节缓冲输出流对象的close方法关闭流释放资源
bos.close();
}
}
package com.itheima._02字节缓冲流基本使用;
import java.io.BufferedOutputStream;
import java.io.FileOutputStream;
/**
目标:理解BufferedOutputStream输出数据的过程
讲解:
1. BufferedOutputStream类构造方法源码分析
在构造方法中创建了一个长度为8192的字节数组:缓冲区数组
2. BufferedOutputStream类write方法源码分析
先将数据保存内部的缓冲区数组中,
当缓冲区数据满了则会将数组的内容输出到目标文件中
3. BufferedOutputStream类close方法源码分析
close方法内部会先调用flush方法刷新缓冲区,然后在关闭流释放资源。
小结:
BufferedOutputStream输出数据的过程小结
* 先调用BufferedOutputStream的write方法将数据输出到缓冲区数组中
* 当缓冲区数组满了或调用close方法或调用flush方法,会调用FileOutputStream
的write方法将缓冲区数组的数据一次性输出到目标文件中。
*/
public class Demo022 {
public static void main(String[] args) throws Exception {
// 1. 创建字节输出流对象并关联目标文件:FileOutputStream
FileOutputStream fos = new FileOutputStream("a.txt");
// 2. 根据字节输出流对象创建字节缓冲输出流:BufferedOutputStream
BufferedOutputStream bos = new BufferedOutputStream(fos);
// 3. 调用字节缓冲输出流对象的write方法输出数据:一个字节、字节数组
bos.write(97);
bos.write("你好".getBytes());
// 4. 调用字节缓冲输出流对象的close方法关闭流释放资源
bos.close();
}
}
package com.itheima._02字节缓冲流基本使用;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
/**
目标:使用字节输入缓冲流BufferedInputStream读取数据
讲解:
1. BufferedInputStream和FileInputStream使用的区别
BufferedInputStream继承InputStream:以字节为单位读取数据。
BufferedInputStream和FileInputStream使用的区别在于构造方法不一致,
其他都是完全一致。
2. BufferedInputStream类常用构造方法
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in,int size)
* 根据字节输入流(非缓冲流)创建字节缓冲输入流对象
* 目前可以传递的子类对象有:FileInputStream
小结:
BufferedInputStream流使用步骤:
1. 创建字节输入流对象关联文件:FileInputStream
2. 根据字节输入流对象创建字节缓冲输入流:BufferedInputStream
3. 调用字节缓冲输入流对象的read方法读取数据
4. 调用字节缓冲输入流对象的close方法关闭流释放资源
*/
public class Demo023 {
public static void main(String[] args) throws Exception {
test01();
// test02();
}
// 字节缓冲输入流一次读取一个字节数组
public static void test02() throws Exception {
// 1. 创建字节输入流对象关联文件:FileInputStream
FileInputStream fis = new FileInputStream("a.txt");
// 2. 根据字节输入流对象创建字节缓冲输入流:BufferedInputStream
BufferedInputStream bis = new BufferedInputStream(fis);
// 3. 调用字节缓冲输入流对象的read方法读取数据
// 创建字节数组:用来存储读取到的数据
byte[] buf = new byte[1024];
// 定义整型变量:接收实际读取到的字节个数
int len = -1;
while ((len = bis.read(buf)) != -1){
System.out.print(new String(buf,0,len));
}
// 4. 调用字节缓冲输入流对象的close方法关闭流释放资源
bis.close();
}
// 字节缓冲输入流一次读取一个字节
public static void test01()throws Exception{
// 1. 创建字节输入流对象关联文件:FileInputStream
FileInputStream fis = new FileInputStream("a.txt");
// 2. 根据字节输入流对象创建字节缓冲输入流:BufferedInputStream
BufferedInputStream bis = new BufferedInputStream(fis);
// 3. 调用字节缓冲输入流对象的read方法读取数据
// 定义整型变量:接收实际读取到的字节数
int len = -1;
while ((len = bis.read()) != -1){
System.out.print((char) len);
}
// 4. 调用字节缓冲输入流对象的close方法关闭流释放资源
bis.close();
}
}
package com.itheima._02字节缓冲流基本使用;
import java.io.BufferedInputStream;
import java.io.FileInputStream;
/**
目标:理解BufferedInputStream读取数据的过程
讲解:
1. BufferedInputStream类构造方法源码分析
内部创建了一个长度为8192的字节数组:缓冲区数组
2. BufferedInputStream类read方法源码分析
先从缓冲区数组中读取数据,如果缓冲区数组没有数据了,则会调用
FileInputStream对象的read从目标文件中读取数据到缓冲区数组中,然后
BufferedInputStream就可以从缓冲区数组中读取数据了
小结:
BufferedInputStream读取数据的过程小结
1. 先利用FileInputStream从目标文件中一次性读取8192字节的数组到缓冲区数组中
2. 然后BufferedInputStream从缓冲区数组中读取数据到程序中使用,如果缓冲区数组的数据
读取完毕,又会利用FileInputStream从目标文件中读取数据到缓冲区数组中。
*/
public class Demo024 {
public static void main(String[] args) throws Exception{
// 创建字节缓冲输入流对象
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("a.txt"));
// 读取数据
System.out.println((char) bis.read());
// 关闭流释放资源
bis.close();
}
}
3.文件复制效率测试
package com.itheima._03文件复制效率测试;
import java.io.*;
/**
目标:测试缓冲流和非缓冲流的执行效率
小结:
缓冲流读写速度远远高于非缓冲流
*/
public class Demo03 {
public static void main(String[] args) throws Exception {
File src = new File("/Users/pkxing/documents/aaaa.mp4");
copyFile01(src,new File("/Users/pkxing/documents/bbbb.mp4"));
copyFile02(src,new File("/Users/pkxing/documents/cccc.mp4"));
copyFile03(src,new File("/Users/pkxing/documents/dddd.mp4"));
}
/**
* 文件复制:缓冲流
* @param src 源文件
* @param dest 目标文件
* @throws Exception
*/
public static void copyFile03(File src,File dest) throws Exception{
long start = System.currentTimeMillis();
// 1. 创建字节输入流关联源文件:必须要存在
BufferedInputStream fis = new BufferedInputStream(new FileInputStream(src));
// 2. 创建字节输出流关联目标文件:可以没有
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(dest));
// 3. 使用字节输入流循环读取源文件数据
// 定义整型变量:接收实际读取到的字节个数
int len = -1;
while ((len = fis.read()) != -1){
// 4. 使用字节输出流将数据输出到目标文件中
fos.write(len);
}
// 5. 关闭流释放资源
fis.close();
fos.close();
long end = System.currentTimeMillis();
System.out.println("缓冲流复制文件一次读写一个字节:" + (end - start));
}
/**
* 文件复制:缓冲流
* @param src 源文件
* @param dest 目标文件
* @throws Exception
*/
public static void copyFile02(File src,File dest) throws Exception{
long start = System.currentTimeMillis();
// 1. 创建字节输入流关联源文件:必须要存在
BufferedInputStream fis = new BufferedInputStream(new FileInputStream(src));
// 2. 创建字节输出流关联目标文件:可以没有
BufferedOutputStream fos = new BufferedOutputStream(new FileOutputStream(dest));
// 3. 使用字节输入流循环读取源文件数据
// 创建字节数组:用来存储读取的数据
byte[] buf = new byte[1024];
// 定义整型变量:接收实际读取到的字节个数
int len = -1;
while ((len = fis.read(buf)) != -1){
// 4. 使用字节输出流将数据输出到目标文件中
fos.write(buf,0,len);
}
// 5. 关闭流释放资源
fis.close();
fos.close();
long end = System.currentTimeMillis();
System.out.println("缓冲流复制文件一次读写一个字节数组:" + (end - start));
}
/**
* 文件复制:非缓冲流
* @param src 源文件
* @param dest 目标文件
* @throws Exception
*/
public static void copyFile01(File src,File dest) throws Exception{
long start = System.currentTimeMillis();
// 1. 创建字节输入流关联源文件:必须要存在
FileInputStream fis = new FileInputStream(src);
// 2. 创建字节输出流关联目标文件:可以没有
FileOutputStream fos = new FileOutputStream(dest);
// 3. 使用字节输入流循环读取源文件数据
// 创建字节数组:用来存储读取的数据
byte[] buf = new byte[1024];
// 定义整型变量:接收实际读取到的字节个数
int len = -1;
while ((len = fis.read(buf)) != -1){
// 4. 使用字节输出流将数据输出到目标文件中
fos.write(buf,0,len);
}
// 5. 关闭流释放资源
fis.close();
fos.close();
long end = System.currentTimeMillis();
System.out.println("非缓冲流复制文件一次读写一个字节数组:" + (end - start));
}
}
输出结论 缓冲流的字节数组快一点
4.字符缓冲流基本使用
package com.itheima._04字符缓冲流基本使用;
import java.io.BufferedWriter;
import java.io.FileWriter;
/**
目标:掌握字符缓冲输出流BufferedWriter类特有的方法
讲解:
1. FileWriter和BufferedWriter使用的区别
BufferedWriter继承Writer:以字符为单位输出数据
FileWriter和BufferedWriter使用的区别在构造方法不一致,其他完全相同。
2. BufferedWriter类构造方法
BufferedWriter(Writer w)
* 根据字符输出流(非缓冲流)创建字符缓冲输出流
* 目前可以传递的子类对象:FileWriter
3. BufferedWriter特有方法
* void newLine() 输出一个换行符
小结:
BufferedWriter类特有的方法:newLine
*/
public class Demo041 {
public static void main(String[] args) throws Exception{
// 1. 创建字符输出流对象:FileWriter
FileWriter fw = new FileWriter("b.txt");
// 2. 根据字符输出流对象创建字符缓冲输出流:BufferedWriter
BufferedWriter bw = new BufferedWriter(fw);
// 3. 调用字符缓冲输出流对象的write方法输出到目标文件中
bw.write("你好世界1");
// 输出一个换行符
bw.newLine();
bw.write("你好世界2");
// 4. 调用字符缓冲输出流对象的close方法关闭流释放资源
bw.close();
}
}
package com.itheima._04字符缓冲流基本使用;
import java.io.BufferedReader;
import java.io.FileReader;
/**
目标:掌握字符缓冲输入流BufferedReader类特有的方法
讲解:
1. FileReader和BufferedReader使用的区别
BufferedReader继承Reader:以字符为单位读取数据
FileReader和BufferedReader使用的区别在于构造房方法不一致,其他完全一致
2. BufferedReader类构造方法
BufferedReader(Reader r)
* 根据字符输入流(非缓冲流)创建字符缓冲输入流对象
* 目前可以传递的子类对象:FileReader
3. BufferedReader特有方法
* String readLine(); 读取一行数据
小结:
BufferedReader类特有的方法:readLine
*/
public class Demo042 {
public static void main(String[] args) throws Exception {
// 1. 创建字符输入流对象关联目标文件:FileReader
FileReader fr = new FileReader("a.txt");
// 2. 根据字符输入流创建字符缓冲输入流:BufferedReader
BufferedReader br = new BufferedReader(fr);
// 3. 调用字符缓冲输入流对象的方法读取数据
/*String line = br.readLine();
System.out.println(line);
line = br.readLine();
System.out.println(line);
line = br.readLine();
System.out.println(line);*/
// 使用循环改进
// 定义字符串接收读取到的每一行数据
String line = null;
while((line = br.readLine()) != null){
System.out.println(line);
}
// 4. 调用字符缓冲输入流对象的close方法关闭流释放资源
br.close();
}
}
5.字符缓冲流练习
package com.itheima._05字符缓冲流练习;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.FileReader;
import java.io.FileWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
/**
使用字符缓冲流完成下面功能:
1. 需求:现有文本文件a.txt,内容如下:
3.侍中、侍郎郭攸之、费祎、董允等,此皆良实,志虑忠纯,是以先帝简拔以遗陛下。愚以为宫中之事,事无大小,悉以咨之,然后施行,必得裨补阙漏,有所广益。
8.愿陛下托臣以讨贼兴复之效,不效,则治臣之罪,以告先帝之灵。若无兴德之言,则责攸之、祎、允等之慢,以彰其咎;陛下亦宜自谋,以咨诹善道,察纳雅言,深追先帝遗诏,臣不胜受恩感激。
4.将军向宠,性行淑均,晓畅军事,试用之于昔日,先帝称之曰能,是以众议举宠为督。愚以为营中之事,悉以咨之,必能使行阵和睦,优劣得所。
2.宫中府中,俱为一体,陟罚臧否,不宜异同。若有作奸犯科及为忠善者,宜付有司论其刑赏,以昭陛下平明之理,不宜偏私,使内外异法也。
1.先帝创业未半而中道崩殂,今天下三分,益州疲弊,此诚危急存亡之秋也。然侍卫之臣不懈于内,忠志之士忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气,不宜妄自菲薄,引喻失义,以塞忠谏之路也。
9.今当远离,临表涕零,不知所言。
6.臣本布衣,躬耕于南阳,苟全性命于乱世,不求闻达于诸侯。先帝不以臣卑鄙,猥自枉屈,三顾臣于草庐之中,咨臣以当世之事,由是感激,遂许先帝以驱驰。后值倾覆,受任于败军之际,奉命于危难之间,尔来二十有一年矣。
7.先帝知臣谨慎,故临崩寄臣以大事也。受命以来,夙夜忧叹,恐付托不效,以伤先帝之明,故五月渡泸,深入不毛。今南方已定,兵甲已足,当奖率三军,北定中原,庶竭驽钝,攘除奸凶,兴复汉室,还于旧都。此臣所以报先帝而忠陛下之职分也。至于斟酌损益,进尽忠言,则攸之、祎、允之任也。
5.亲贤臣,远小人,此先汉所以兴隆也;亲小人,远贤臣,此后汉所以倾颓也。先帝在时,每与臣论此事,未尝不叹息痛恨于桓、灵也。侍中、尚书、长史、参军,此悉贞良死节之臣,愿陛下亲之信之,则汉室之隆,可计日而待也。
2. 要求将a.txt文件的内容复制到b.txt文件中并恢复行号的顺序。
*/
public class Demo05 {
public static void main(String[] args) throws Exception{
// 1. 创建字符缓冲输入流关联源文件
BufferedReader br = new BufferedReader(new FileReader("a.txt"));
// 2. 创建字符缓冲输出流关联目标文件
BufferedWriter bw = new BufferedWriter(new FileWriter("b.txt"));
// 3. 创建集合存储读取到每一行数据
ArrayList<String> list = new ArrayList<>();
// 4. 使用循环读取每行数据到集合中
// 4.1 定义字符接收每一行数据
String line = null;
// 4.2 循环读取
while((line = br.readLine()) != null){
// 4.3 将读取到的数据添加到集合中
list.add(line);
}
// 5. 使用工具类方法对集合元素排序
Collections.sort(list);
// 6. 遍历集合将每一个元素输出到目标文件中
for (String content : list) {
// 利用bw将content输出目标文件中
bw.write(content);
// 输出一个换行符
bw.newLine();
}
// 7. 关闭流释放资源
br.close();
bw.close();
}
}
6.编码表概述
package com.itheima._06编码表概述;
/**
详情参见预习资料,笔记文件夹下的:编码表概述.md 文件
目标:能够说出编码表的意义
小结:
编码表的意义:
用来记录字符与其二进制的对应关系
*/
public class Demo06 {
}
在计算机中无论任何数据的传输、存储、持久化,都是以二进制的形式体现的。
那么当我存一个字符的时候,计算机需要持久化到硬盘,或者保存在内存中。
这个时候保存在内存、硬盘的数据显然也是二进制的。
那么当我需要从硬盘、内存中取出这些字符,再显示的时候,为什么二进制会变成了字符呢?
Map<字符,数字>
码表可以理解为:键和值都是唯一的map集合
a 97
b 98
什么时候会使用到码表?
存储数据时:编码,将字符转换为二进制数据的过程
a ==> 97 ==> 01011001
读取数据时:解码,将二进制数据转换为字符的过程
01011001 ==> 97 ==> a
这就是码表存在的意义。
码表其实就是一个字符和其对应的二进制相互映射的一张表。
这张表中规定了字符和二进制的映射关系。
计算机存储字符时将字符查询码表,然后存储对应的二进制。
计算机取出字符时将二进制查询码表,然后转换成对应的字符显示。
不同的码表所容纳的字符映射也是不同的。
可以这样理解。
在有些码表中一个字符占用1个字节,1个字节能表示的范围是-128到127,总共为256。所以能容纳256个字符映射。
而有些码表中一个字符占用2个字节,甚至3个字节,因此能容纳的字符映射也更多。
下面按照自己的理解详细讲述一下不同的码表。
常见的码表:
ASCII:
* 美国码表,码表中只有英文大小写字母、数字、美式标点符号等。每个字符占用1个字节,所有字符映射的二进制都为正数,因此有128个字符映射关系。
GB2312:
* 兼容ASCII码表,并加入了中文字符,码表中英文大小写字母、数字、美式标点符号占一个字节,中文占两个字节,中文映射的二进制都是负数,因此有128× 128 = 16384个字符映射关系。
你 -100 -125
GBK/GB18030:
* 兼容GB2312码表,英文大小写字母、数字、美式标点符号,占一个字节。中文占两个字节,第一个字节为负数,第二个字节为正数和负数,因为有128× 256 = 32768个字符映射关系。
你 -100 125
128 * 256
Unicode码表:
* 国际码表,包含各国大多数常用字符,每个字符都占2个字节,因此有65536个字符映射关系。Java语言使用的就是Unicode码表。
* Java中的char类型用的就是这个码表。char c = 'a';占两个字节。
UTF-8码表:
* 是基于Unicode码表的,但更智能,会根据字符的内容选择使用多个字节存储。英文占一个字节,中文占3个字节。
你 -111 -122 -113
乱码的原因
* 因为文本在存储时使用的码表和读取时使用的码表不一致造成的。
7.字符流读取字符乱码问题
package com.itheima._07字符流读取字符乱码问题;
import java.io.FileReader;
/**
目标:能够说出FileReader和BufferedReader读取字符乱码的原因
讲解:
小结:
1. 字符流读取字符乱码的原因?编码和解码时使用的码表不一致
2. 如何解决乱码问题?要使用相同的码表进行编码和解码
*/
public class Demo07 {
public static void main(String[] args) throws Exception {
// 创建FileReader对象
// 默认使用的utf8解码
FileReader fr = new FileReader("a.txt");
// 读取字符数据
int len = -1;
while ((len = fr.read()) != -1){
System.out.print((char) len);
}
// 关闭流释放资源
fr.close();
}
}
8.转换流基本使用
//转换流 字符转字节 字节本身不会编码 所以我要getbyte 而字符可以制动编码解码
FileOutputStream outputStream = new FileOutputStream("a.txt");
OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream);
outputStreamWriter.write("您好");
outputStreamWriter.close();
package com.itheima._08转换流基本使用;
import java.io.FileInputStream;
import java.io.InputStreamReader;
/**
目标:使用转换流:InputStreamReader读取字符数据保证不乱码
讲解:
1. InputStreamReader概述
继承Reader,本质字符输入流,以字符为单位读取数据
InputStreamReader 是字节流通向字符流的桥梁:字节转字符
2. InputStreamReader类的构造方法
InputStreamReader(InputStream in)
* 根据字节输入流对象创建字符转换输入流对象
* 目前可以传递的字节输入流对象有:FileInputStream和BufferedInputStream
InputStreamReader(InputStream in, String charsetName)
* 根据字节输入流对象创建字符转换输入流对象
* 目前可以传递的字节输入流对象有:FileInputStream和BufferedInputStream
* charsetName:字符集名称,就是编码表名称,常用名称:gbk和utf-8
3. InputStreamReader常用方法
read:读一个字符,一个字符数组
注意:字节流不会查码表,只有字符流才查询码表
FileReader ==> FileInputStream
BufferedReader
小结:
InputStreamReader流读取数据的过程:
1. 先通过FileInputStream对象从目标文件中读取数据,读取出来的是一堆二进制数据
2. 然后将二进制数据交给InputStreamReader对象查询指定的码表:将二进制数据转换字符
*/
public class Demo081 {
public static void main(String[] args) throws Exception{
// 1.创建字节输入流关联目标文件
FileInputStream fis = new FileInputStream("a.txt");
// 2.根据字节输入流创建字符转换输入流:默认码表utf8
// InputStreamReader isr = new InputStreamReader(fis);
// 2.根据字节输入流创建字符转换输入流:指定码表为gbk
InputStreamReader isr = new InputStreamReader(fis,"utf8");
// 3.调用字符转换输入流的read方法读取数据
char[] buf = new char[3];
int len = isr.read(buf);
System.out.println(len);
System.out.println(new String(buf,0,len));
// 4.调用字符转换输入流的close方法关闭流释放资源
isr.close();
}
}
package com.itheima._08转换流基本使用;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
/**
目标:使用转换流:OutputStreamWriter输出字符数据
讲解:
1. OutputStreamWriter概述
继承Writer:本质字符输出流,以字符为单位输出数据
OutputStreamWriter 是字符流通向字节流的桥梁:字符转字节
2. OutputStreamWriter类的构造方法
OutputStreamWriter(OutputStream out)
OutputStreamWriter(OutputStream out,String charsetName)
* 根据字节输出流对象创建字符转换输出流
* 目前可以传递的字节输出流有:FileOutputStream和BufferedOutputStream
* charsetName:指定码表名称,码表常用有gbk和utf-8
小结:
OutputStreamWriter流输出数据的过程:
1. 先通过OutputStreamWriter将数据查询指定的码表转换为二进制数据
2. 然后将二进制数据交个FileOutputStream输出到目标文件中
*/
public class Demo082 {
public static void main(String[] args)throws Exception {
// 1. 创建字节输出流对象关联目标文件
FileOutputStream fos = new FileOutputStream("c.txt");
// 2. 创建字符转换输出流对象:默认使用utf8码表
// OutputStreamWriter osw = new OutputStreamWriter(fos);
// 2. 创建字符转换输出流对象:默认使用utf8码表
OutputStreamWriter osw = new OutputStreamWriter(fos,"gbk");
// 3. 调用字符转换输出流的write方法输出数据
osw.write("你好"); //
osw.write("\r\n");
osw.write("你好");
// 4. 关闭字符转换输出流释放资源
osw.close();
}
}
9.对象序列化流
package com.itheima._09对象序列化流;
/**
目标: 掌握对象序列化和反序列化的概念
讲解:
1. 对象序列化和反序列化的概念
对象序列化: 将自定义对象以流的形式保存到文件的过程
对象反序列化:将自定义对象从文件中读取出来的过程
2. 实现对象序列化和反序列化相关的类
ObjectOutputStream:对象输出流:将自定义对象保存到文件实现序列化操作
ObjectInputStream:对象输入流:将文件中的对象读取到内存中实现反序列化操作。
小结:
1. 什么是对象序列化和反序列化?
对象序列化:保存自定义对象
对象反序列化:读取自定义对象
*/
public class Demo090 {
}
package com.itheima._09对象序列化流;
import java.io.FileOutputStream;
import java.io.ObjectOutputStream;
/**
目标:使用对象输出流将对象保存到文件中:实现对象序列化操作
讲解:
1. ObjectOutputStream构造方法
* ObjectOutputStream 继承 OutputStream
* ObjectOutputStream(OutputStream out)
* 根据字节输出流创建对象输出流
* 可以传递的字节输出流有:FileOutputStream和BufferedOutputStream
2. ObjectOutputStream类常用方法
* void writeObject(Object obj)
* 保存对象到流关联的目标文件中
3. ObjectOutputStream流的使用步骤
1.创建字节输出流关联目标文件:FileOutputStream
2.根据字节输出流创建对象输出流:ObjectOutputStream
3.创建自定义封装数据:Student
4.调用对象输出流的writeObject方法传递Student对象
5.调用对象输出流的close方法关闭流释放资源
4. 异常信息:
java.io.NotSerializableException: com.itheima._09对象序列化流.Student
如果需要将对象通过对象输出流实现序列化操作则要求对象要实现接口:Serializable
5. Serializable接口概述
Serializable是一个标记性接口,标记实现该接口的类就可以被序列化。
小结:
1. 被序列化的对象需要实现什么接口:Serializable
2. 将对象序列化到文件的方法是:writeObject
*/
public class Demo091 {
public static void main(String[] args)throws Exception {
// 1.创建字节输出流关联目标文件:FileOutputStream
FileOutputStream fos = new FileOutputStream("stu.txt");
// 2.根据字节输出流创建对象输出流:ObjectOutputStream
ObjectOutputStream oos = new ObjectOutputStream(fos);
// 3.创建自定义封装数据:Student
Student stu = new Student("jack", 20);
// 设置身份证号
stu.setIdCard("4403423419981010214324");
// 4.调用对象输出流的writeObject方法传递Student对象
oos.writeObject(stu);
// 5.调用对象输出流的close方法关闭流释放资源
oos.close();
}
}
package com.itheima._09对象序列化流;
import java.io.FileInputStream;
import java.io.ObjectInputStream;
/**
目标:使用对象输入流从文件中读取对象:实现对象反序列化操作
讲解:
1. ObjectInputStream构造方法
ObjectInputStream继承InputStream,字节输入流
ObjectInputStream(InputStream in)
* 根据字节输入流创建对象输入流
* 目前可以传递的子类有:FileInputStream和BufferedInputStream
2. ObjectInputStream类常用方法
* Object readObject(); 从流关联的目标文件中读取一个对象
3. ObjectInputStream流的使用步骤
1. 创建字节输入流关联目标文件:FileInputStream
2. 根据字节输入流创建对象输入流:ObjectInputStream
3. 调用对象输入流的readObject方法读取对象
4. 调用close方法关闭对象输入流释放资源
小结:
1. 实现对象反序列化需要调用对象输入流的哪个方法
readObject
*/
public class Demo092 {
public static void main(String[] args) throws Exception{
// 1. 创建字节输入流关联目标文件:FileInputStream
FileInputStream fis = new FileInputStream("stu.txt");
// 2. 根据字节输入流创建对象输入流:ObjectInputStream
ObjectInputStream ois = new ObjectInputStream(fis);
// 3. 调用对象输入流的readObject方法读取对象
// new Student("jack",20);
Object obj = ois.readObject();
System.out.println(obj);
// 4. 调用close方法关闭对象输入流释放资源
ois.close();
}
}
package com.itheima._09对象序列化流;
/**
目标:掌握transient关键字的作用
讲解:
1. transient关键字的作用
transient:瞬时,短暂 持久化操作
用来修饰成员变量,该成员变量的值不会被序列化到文件中
2. transient使用格式
修饰符 transient 数据类型 成员变量;
小结:
1. transient关键字的作用:修饰成员变量,保存成员变量的值不会被序列化到文件中
*/
public class Demo093 {
}
package com.itheima._09对象序列化流;
import java.io.Serializable;
/*
目标:理解序列号冲突的原因以及解决方案
小结:
1. 序列号冲突的原因:保存对象时和读取对象时序列号不一致
2. 如何解决序列号冲突:
自定义序列号,自己提供序列号不要让JVM随机生成了。
自定义格式: private static final long serialVersionUID = 1L;
*/
/*
java.io.InvalidClassException: 无效类异常,序列号冲突了
com.itheima._09对象序列化流.Student; local class incompatible:
stream classdesc serialVersionUID = -5225972477702851865,
local class serialVersionUID = -5126238547521328132
*/
// 当类实现Serializable接口时,每次编译JVM内部都会随机为该类生成一个序列号
// 序列号就是一个长整型的整数,可以理解为类的身份证号。
public class Student implements Serializable {
// 自定义序列号
private static final long serialVersionUID = 1L;
private String name;
private int age;
private transient String idCard; // 身份证号
public String getIdCard() {
return idCard;
}
public void setIdCard(String idCard) {
this.idCard = idCard;
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
public Student() {
}
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;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", idCard='" + idCard + '\'' +
'}';
}
}
10.练习序列化
package com.itheima._10练习_序列化集合;
import com.itheima._09对象序列化流.Student;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.ArrayList;
/**
目标:能够将集合序列化到文件中
需求:
1. 将存有多个自定义对象的集合序列化操作,保存到list.txt文件中。
2. 反序列化list.txt ,并遍历集合,打印对象信息。
小结:
1. 集合序列化到文件中的注意事项:集合中对象也需要实现Serializable接口
*/
public class Demo10 {
public static void main(String[] args)throws Exception{
// 保存集合到文件中
// saveArrayList();
// 从文件中读取集合
readArrayList();
}
// 保存集合到文件中
public static void saveArrayList()throws Exception{
// 创建集合对象
ArrayList<Student> list = new ArrayList<>();
// 添加多个学生
list.add(new Student("rose", 20));
list.add(new Student("lucy", 18));
list.add(new Student("lily", 22));
// 创建对象输出流
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("list.txt"));
// 保存集合到文件中
// 要求:集合中的元素对象也必须实现Serializable接口
oos.writeObject(list);
// 关闭流
oos.close();
}
// 从文件中读取集合
public static void readArrayList() throws Exception{
// 创建对象输入流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("list.txt"));
// 调用方法读取集合
ArrayList<Student> list = (ArrayList<Student>) ois.readObject();
for (Student stu : list) {
System.out.println(stu);
}
// 关闭流释放资源
ois.close();
}
}
11.打印基本流基本使用
package com.itheima._11打印流基本使用;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
目标:能够使用打印流打印数据
97 ==> a
97 ==> 97
讲解:
1. 打印流分类
字节打印流:PrintStream
字符打印流:PrintWriter
2. 打印流的特点
* 方便原样输出各种数据类型的值
* 只有输出流,没有输入流
3. PrintStream类使用
* print(数据类型 变量名) 输出数据,不会换行
* println(数据类型 变量名) 输出数据,会自动换行
小结:
1. 打印流的特点:只有输出,没有输入,能够原样输出数据
2. 打印流的常用方法
println
*/
public class Demo11 {
public static void main(String[] args) throws Exception {
// 创建打印流对象:字节打印流
// PrintStream ps = new PrintStream("ps.txt");
// 创建打印流对象:字符打印流
PrintWriter ps = new PrintWriter("ps.txt");
// 打印数据到目标文件中
ps.println(97);
ps.println(1.5);
ps.println(false);
ps.println("abc");
ps.println('a');
// 关闭流释放资源
ps.close();
}
}
package com.itheima._11打印流基本使用;
import java.io.PrintStream;
import java.io.PrintWriter;
/**
目标:能够使用打印流打印数据
*/
public class Demo12 {
public static void main(String[] args) throws Exception {
// 调⽤用系统的打印流,控制台直接输出97
System.out.println(97);
PrintStream consolePs = System.out;
/*
String name = stu.getName(); // jack
stu.setName(name)
*/
// 创建打印流,指定⽂文件的名称
PrintStream ps = new PrintStream("ps.txt");
// 设置系统的打印流流向,输出到ps.txt
System.setOut(ps);
// 调⽤用系统的打印流,ps.txt中输出97
System.out.println(97);
//回到控制台
System.setOut(consolePs);
System.out.println(98);
}
}
12.IO流小结
package com.itheima._12IO流小结;
/**
IO流小结
字节流输入流:InputStream 所有字节输入流的父类,抽象类
FileInputStream:非缓冲流,效率低,不推荐直接使用
BufferedInputStream:缓冲流,效率高,推荐直接使用
ObjectInputStream:对象输入流,实现对象反序列化操作时使用,否则不使用。
字节流输出流:OutputStream 所有字节输出流的父类,抽象类
FileOutputStream:非缓冲流,效率低,不推荐直接使用
BufferedOutputStream:缓冲流,效率高,推荐直接使用
ObjectOutputStream:对象输入流,实现对象序列化操作时使用,否则不使用
PrintStream:打印流,希望原样输出数据时使用,其他不用
字符流输入流:Reader:所有字符输入流的父类,抽象类
FileReader:非缓冲流,效率低,不推荐直接使用
BufferedReader:缓冲流,效率高,推荐直接使用
InputStreamReader:字符转换输入流,当需要修改默认码表时使用,否则不用。
字符流输出流:Writer:所有字符输出流的父类,抽象类
FileWriter:非缓冲流,效率低,不推荐直接使用
BufferedWriter:缓冲流,效率高,推荐直接使用
OutputStreamWriter:字符转换输出流,当需要修改默认码表输出数据时使用,否则不用。
PrintWriter:打印流,希望原样输出数据时使用,其他不用
字节流可以操作任意类型的数据:文本文件,图片,视频,音频......
字符流只能操作文本数据:文本文件
如何选择IO流
* 如果明确清楚要操作的文件类型是文本文件则强烈推荐选择字符流
* 判断是输入操作还是输出操作,输入操作则选择字符输入流
* 否则就是字符输出流
* 如果不清楚文件类型时,则只能选择字节流。
*/
public class Demo12 {
}
13.IO流异常处理
package com.itheima._13IO流异常的处理;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
/**
目标: 掌握IO异常的正确处理方式
讲解:
1. JDK1.7之前的处理方式
2. JDK1.7之后的处理方式
小结:
1. JDK1.7之前的IO异常处理方式
try{}catch(Exception e){} finally{ 关闭流 }
2. JDK1.7之后的IO异常处理方式
try(创建流){ } catch(Exception e){}
*/
public class Demo13 {
/**
* JDK1.7之后IO异常处理格式
try(创建流的代码){
} catch(异常类名 表名){
}
小括号只能放创建流的代码,其他代码不能写在里面
*/
public static void copyFile02(File src, File dest) {
try( // 1. 创建字节输入流关联源文件:必须要存在
FileInputStream fis = new FileInputStream(src);
// 2. 创建字节输出流关联目标文件:可以没有
FileOutputStream fos = new FileOutputStream(dest);){
// 3. 使用字节输入流循环读取源文件数据
// 创建字节数组:用来存储读取的数据
byte[] buf = new byte[1024];
// 定义整型变量:接收实际读取到的字节个数
int len = -1;
while ((len = fis.read(buf)) != -1){
// 4. 使用字节输出流将数据输出到目标文件中
fos.write(buf,0,len);
}
} catch(Exception e){
e.printStackTrace();
}
}
/**
* JDK1.7之前IO异常处理的格式
try{
} catch(异常类名 变量名){
} finally{
关闭流释放资源
}
*/
public static void copyFile01(File src, File dest) {
// 声明字节输入流
FileInputStream fis = null;
// 声明字节输出流
FileOutputStream fos = null;
try{
// 1. 创建字节输入流关联源文件:必须要存在
fis = new FileInputStream(src);
// 2. 创建字节输出流关联目标文件:可以没有
fos = new FileOutputStream(dest);
// 3. 使用字节输入流循环读取源文件数据
// 创建字节数组:用来存储读取的数据
byte[] buf = new byte[1024];
// 定义整型变量:接收实际读取到的字节个数
int len = -1;
while ((len = fis.read(buf)) != -1){
// 4. 使用字节输出流将数据输出到目标文件中
fos.write(buf,0,len);
}
} catch(Exception e){
e.printStackTrace();
}finally {
// 5. 关闭流释放资源
try {
if (fis != null)
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fos != null)
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
14.Properties集合使用
package com.itheima._14Properties集合使用;
import java.util.Properties;
import java.util.Set;
/**
目标:使用Properties集合进行增删改数据
讲解:
1. Properties类概述
* 继承Hashtable,Hashtable类已经过时,被HashMap取代
* Hashtable是线程安全,效率低
* 实现了Map接口,就是一个双列集合,和HashMap用法一致。
* Properties集合在使用时不需要指定键和值类型,默认都是String
* Properties集合有和流技术相结合的方法
* 直接将集合中的数据保存到文件中
* 直接从文件中读取数据到集合中
2. Properties类常用方法
* Object setProperty(String key,String value)
* 添加键值对到集合中
* 键存在则使用新值替换旧值,返回旧值,否则返回null
* String getProperty(String key)
* 根据键获得值,如果键不存在返回null
* Set<String> stringPropertyNames()
* 获得键集合
小结:
1. Object setProperty(String key,String value)的作用?
添加键值对
2. String getProperty(String key)的作用?
根据键获得值
3. Set<String> stringPropertyNames()的作用?
获得键集合
*/
public class Demo141 {
public static void main(String[] args) {
// 创建属性集合
Properties info = new Properties();
// 添加键值对数据
info.setProperty("name", "zhangsan");
info.setProperty("name", "lisi");
info.setProperty("gender", "男");
info.setProperty("age", "30");
System.out.println(info);
// 根据键获得值
System.out.println(info.getProperty("name")); // lisi
// 根据键删除键值对
info.remove("gender");
System.out.println(info);
System.out.println("---------------");
// 遍历集合
Set<String> keySet = info.stringPropertyNames();
for (String key : keySet) {
System.out.println(key+"="+info.getProperty(key));
}
}
}
package com.itheima._14Properties集合使用;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Properties;
/**
目标:
1.将Properties集合元素保存到文件中
2.从文件中读取数据到Properties集合中
讲解:
1. Properties类与流相结合使用的方法
* void store(OutputStream out,String comment)
* 将集合数据保存到流关联的目标文件中
* comment:说明信息,一般给null
* void load(InputStream in)
* 从流关联的目标文件中读取数据到集合中
2. 属性文件概述
* 文件名要求:xxx.properties
* 一个键值对占据一行:格式:键=值
* 文件中可以使用注释,注释是以 # 开头
小结:
1. 可以将Properties集合数据保存到文件的方法是哪个? store
2. 可以从文件中加载数据到Properties集合的方法是哪个?load
*/
public class Demo142 {
public static void main(String[] args) throws Exception{
// save();
read();
}
/**
* 读取文件中数据到Properties集合中
*/
private static void read()throws Exception {
// 创建属性集合对象
Properties info = new Properties();
System.out.println(info);
// 创建字节输入流关联目标文件
FileInputStream fis = new FileInputStream("day10_课堂代码/jdbc.properties");
// 从目标文件中加载数据
info.load(fis);
System.out.println(info);
// 关闭流释放资源
fis.close();
}
/**
* 保存Properties集合数据到文件中
*/
private static void save() throws IOException {
// 创建属性集合
Properties info = new Properties();
info.setProperty("name", "jack");
info.setProperty("gender", "男");
// 创建字节输入流关联目标文件
FileOutputStream fos = new FileOutputStream("stu.properties");
// 保存集合数据到文件中
// 参数1:输出流
// 参数2:说明信息,一般给null
info.store(fos,null);
// 关闭流释放资源
fos.close();
}
}
15.总结
- 能够使用Properties的load方法加载文件中配置信息
Properties info = new Properties();
FileInputStream fis = new FileInputStream("xx.properties");
info.load(fis);
fis.close().
- 能够使用字节缓冲流读取数据到程序
1. BufferedInputStream bis = new BufferedInputStream(new FileInputStream("目标文件"));
2. bis.read(byte[] buf);
3. bis.close();
- 能够使用字节缓冲流写出数据到文件
1. BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("目标文件"));
2. bos.write(字节或字节数组);
3. bos.close();
- 能够明确字符缓冲流的作用和基本用法
作用:提高读写效率
- 能够使用缓冲流的特殊功能
BufferedReader:String readLine(); 读取一行数据
BufferedWriter:void newLine(); 输出一个换行符
- 能够阐述编码表的意义
记录字符与其二进制数据的对应关系
a 97
- 能够使用转换流读取指定编码的文本文件
1. InputStreamReader isr = new InputStreamReader(new FileInputStream("目标文件"),"gbk");
2. isr.read(字符数组);
3. isr.close();
- 能够使用转换流写入指定编码的文本文件
1. OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("目标文件"),"gbk");
2. osw.write(字符数组);
3. osw.close();
- 能够使用序列化流写出对象到文件
1. ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("目标文件"));
2. oos.writeObject(Object obj);
3. oos.close();
- 能够使用反序列化流读取文件到程序中
1. ObjectInputStream ois = new ObjectInputStream(new FileInputStream("目标文件"));
2. Object obj = ois.readObject();
3. ois.close();
记得点赞评论哦(* ̄︶ ̄)