day17【缓冲流、转换流、序列化流】
反馈和复习
一切皆字节,字符流能干的事字节流都能干。
但是用字符流干的事情,字节流干的不够专业:例如用字符流读取文本文件时字符流可以使用自己的方法实现读一行写一行
字节流的缺点表现在不能直接写出字符串或者字符,这些都是字符流擅长的事情。
字符流只能读取文本文件,当用字符流复制一个图片时图片可以被复制大小会增大一倍左右,但是不能打开。所以字符流只能读取文本文件包括txt文件。
字节流也能读取文本文件,但是要一个字节一个字节的读取。还需要转码,麻烦
1.这两天知识点太多,有点记差了
2.建议有时间复习一下前面的知识
复习:
1.字符流
FileWriter FileReader
2.ResourceBundle
3.Properties
a.都可以读取Properties配置文件
区别:
a.ResourceBundle是静态方法getBundle,Properties成员方法load
b.ResourceBundle一般读取src根目录下,Properties一般读取项目根目录下
c.ResourceBundle读取时只需要写文件名(不带后缀), Properties读取时文件名要写全名
4.异常处理
JDK1.7之前
try{
}catch(Exception e){
}finally{
xxx.close();
}
JDK1.7以及之后
try(FileReader fr = new FileReader("1.txt")){
}catch(Exception e){
}
今日内容
1.缓冲流(高效流,比普通流性能更高)【重点】
2.转换流(编码相关的流,指定编码) 【重点】
3.序列化流(操作对象) 【理解】
4.打印流(System.out.println()) 【理解】
5.设计模式(装饰设计模式,4个步骤) 【理解】
6.common-io工具包(简化IO的代码) 【重点】
第一章 缓冲流
1.缓冲流的介绍
缓冲流也叫高效流,对我们以前学过的四个基本流的增强(性能,方法上基本一模一样)
2.缓冲流的分类
缓冲字节输入流:BufferedInputStream ---> 对普通的字节输入流InputStream的增强
缓冲字节输出流:BufferedOutputStream ---> 对普通的字节输出流OutputStream的增强
缓冲字符输入流: BufferedReader ---> 对普通的字符输入流Reader的增强
缓冲字符输出流: BufferedWriter ---> 对普通的字符输出流Writer的增强
3.字节缓冲流的介绍和使用【重点】
-
字节缓冲流的构造
public BufferedInputStream(InputStream in);//缓冲流的构造需要接收对应普通流 public BufferedOutputStream(OutputStream out);//缓冲流的构造需要接收对应普通流
-
字符缓冲流的高效测试
public class BufferedDemo01 { public static void main(String[] args) throws Exception { copy01(); } //缓冲流复制 耗时:950毫秒 public static void copy01() throws Exception { //创建缓冲流 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("G:\\upload\\666.png")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy01.png")); //复制文件 long start = System.currentTimeMillis(); int b = 0; while ((b = bis.read()) != -1) { bos.write(b); } long end = System.currentTimeMillis(); System.out.println("耗时:"+(end-start)+"毫秒"); //释放资源 bos.close(); bis.close(); } //普通流复制 半天过去了... public static void copy02() throws IOException { //创建普通字节流 FileInputStream fis = new FileInputStream("G:\\upload\\666.png"); FileOutputStream fos = new FileOutputStream("copy.png"); long start = System.currentTimeMillis(); //复制,一次一个字节 int b = 0; while ((b = fis.read()) != -1) { fos.write(b); } long end = System.currentTimeMillis(); System.out.println("耗时:"+(end-start)+"毫秒"); //释放资源 fos.close(); fis.close(); } } 思考: 有没有更快的方式 ?? 有! 使用缓冲流的同时,使用一次读取一个字节数组的方式!!!(建议课下同学们自己测试)
4.字符缓冲流的介绍和使用【重点】
-
a.字符缓冲流的构造
public BufferedWriter(Writer w);//需要传入普通的字符流 public BufferedReader(Reader r);//需要传入普通的字符流 字符缓冲流也是对普通字符类的性能增强(课下可以自己复制一个文本文件,做比较)
-
b.字符缓冲流的2个特有方法
BufferedWriter 是 普通Writer的增强 多了一个特有方法: 写一个换行符(根据系统而定) public void newLine(); public class BufferedDemo02 { public static void main(String[] args) throws IOException { //缓冲字符流 BufferedWriter bw = new BufferedWriter(new FileWriter("1.txt")); //写数据 for (int i = 0; i < 10; i++) { bw.write("java"); //写入一个换行 bw.newLine(); } //释放资源 bw.close(); //关流的原则: //a.先开后关 //b.流用完了就立刻关闭 } } BufferedReader 是 普通Reader的增强 多个一个特有方法: 一次读取一行内容 public String readLine(); public static void main(String[] args) throws IOException { //缓冲字符流 BufferedReader br = new BufferedReader(new FileReader("2.txt")); //读数据 //String line = br.readLine(); //System.out.println(line); //=============一次读取一行的标准循环代码=============== String line = ""; //用来保存每次读取的一行数据 /** * (line = br.readLine()) != null * 以上代码干了三件事! * a.读取 br.readLine() * b.赋值 line = 读到的一行数据 * c.判断 line != null */ while ((line = br.readLine()) != null) { System.out.println(line); } //释放资源 br.close(); } 注意: 一次读取一行的标准循环,不会因为有一行为"null"字符串内容或者有一行为""字符串内容而停止 只有读取到文件的末尾,没有内容返回值null才能停止!!
5.综合练习:文本排序
读取文本中的内容,按照序号排序,排序之后从头到尾写入新的文件中
public class TestDemo {
public static void main(String[] args) throws Exception {
//读取文本中的内容,按照序号排序,排序之后从头到尾写入新的文件中
ArrayList<String> arr = new ArrayList<String>();
//1.读取文本中的内容
BufferedReader br = new BufferedReader(new FileReader("csb.txt"));
//2.一次读取一行
String line = "";
while ((line = br.readLine()) != null) {
arr.add(line);
}
//3.释放资源
br.close();
//4.对集合排序
// Collections.sort(arr, new Comparator<String>() {
// @Override
// public int compare(String o1, String o2) {
// //按照字符串的首字母进行升序
// return o1.charAt(0) - o2.charAt(0);
// }
// });
Collections.sort(arr, (o1, o2) -> o1.charAt(0) - o2.charAt(0));
//5.写入到新文件中
BufferedWriter bw = new BufferedWriter(new FileWriter("new.txt"));
for (String s : arr) {
bw.write(s);
bw.newLine();//一句话一行
}
bw.close();
}
}
第二章 转换流
1.编码和解码
编码: 把字符 ----> 字节 ,比如 'a' ---> 97(0110 0001)
解码: 把字节 ----> 字符 ,比如 97(0110 0001) ---> 'a'
2.字符集
字符集: 一个系统支持的所有字符的集合(文字,标点,数字,图形符号等)
3.字符编码
字符编码: 字符和二进制一一对应的一套规则,比如 字符'a' 对应的码值 97
常见的字符集和字符编码【重点】
ASCII字符集 ---> ASCII编码, 规定ASCII字符集中所有的字符都占1个字节
GBK字符集 ---> GBK编码,规定所有中文字符都占2个字节(这两个字节都是负数)
Unicode字符集 ---> UTF-8编码,规定所有中文字符都占3个字节
ISO-8859-1字符集.这是西欧国家的字符集,(我们以后使用Tomcat7以前默认就是使用ISO-8859-1)
4.编码引出的问题
IDEA默认使用UTF-8编码,Windows默认使用的GBK编码
public class TestLuanMaDemo {
public static void main(String[] args) throws IOException {
FileReader fr = new FileReader("GBK.txt");//文件用的GBK编码,一个中文两个字节
int ch = fr.read(); //IDEA默认UTF-8编码,一个中文3个字节
System.out.println((char)ch);
fr.close();
}
}
出现这个问题了,怎么办??
a.把文件的编码改成UTF-8和IDEA一致
b.把IDEA的编码改成GBK和文件的一致
4.使用转换流InputStreamReader解决读取中文的问题【重点】
转换输入流 InputStreamReader extends Reader
-
构造方法
public InputStreamReader(InputStream in,String charsetName);//指定使用何种编码读文件 public InputStreamReader(InputStream in);//使用工具默认的编码去读文件(IDEA默认UTF-8)
-
使用InputStreamReader读取不同编码的文件(代码演示)
public class TestInputStreamReaderDemo { public static void main(String[] args) throws Exception { //1.创建InputStreamReader对象 InputStreamReader isr = new InputStreamReader(new FileInputStream("utf8.txt"),"UTF-8"); // InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"GBK"); // InputStreamReader isr = new InputStreamReader(new FileInputStream("utf8.txt"),"GBK"); //2.读数据 int ch = isr.read(); System.out.println((char) ch); ch = isr.read(); System.out.println((char) ch); ch = isr.read(); System.out.println((char) ch); //3.释放资源 isr.close(); } }
5.使用转换流OutputStreamWriter写不同编码的中文【重点】
转换输出流OutputStreamWriter extends Writer
-
构造方法
public OutputStreamWriter(OutputStream out,String charsetName);//写文件时指定编码 public OutputStreamWriter(OutputStream out);//使用工具默认的编码写文本(IDEA是UTF-8)
-
输出指定编码的中文
public class TestOutputStreamWriterDemo { public static void main(String[] args) throws IOException { //1.创建OutputStreamWriter对象 // OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("newgbk.txt"),"GBK"); OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("newutf8.txt"),"UTF-8"); //2.写数据 osw.write("你好"); //3.释放资源 osw.close(); } }
6.转换流的理解
7.练习:转换文件编码【重点】
需求:将GBK编码的文本文件,转换为UTF-8编码的文本文件。
分析:
a.先把GBK文件中内容读取出来(指定以GBK编码读取)
b.再把数据写入到UtF-8文件中(指定以UTF-8编码写入)
public class TestDemo {
public static void main(String[] args) throws IOException {
// 需求:将GBK编码的文本文件,转换为UTF-8编码的文本文件。
// 分析:
// a.先把GBK文件中内容读取出来(指定以GBK编码读取)
InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"GBK");
// b.再把数据写入到UtF-8文件中(指定以UTF-8编码写入)
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("UTF8.txt"),"UTF-8");
//复制
int ch = 0;
while ((ch = isr.read()) != -1) {
osw.write(ch);
}
//释放资源
osw.close();
isr.close();
}
}
第三章 序列化流
1.什么是序列化流
序列化流: 写出对象的流
ObjectOutputStream
反序列化流: 读取对象的流
ObjectInputStream
2.ObjectOutputStream的介绍和使用【重点】
-
构造方法
public ObjectOutputStream(OutputStream out); //需要接收一个普通的字节输出流
-
序列化操作的前提
想要序列化必须实现java.io.Serializable 接口 该接口中没有方法,该接口一般称为标记接口
-
序列化操作(代码演示)
public class TestObjectOutputStreamDemo { public static void main(String[] args) throws IOException { //1.创建一个ObjectOutputStream对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dog.txt")); //2.写对象 Dog dd = new Dog(10,"旺财"); oos.writeObject(dd);//NotSerializableException //3.释放资源 oos.close(); } } 注意: 不需要查看dog.txt文件,因为这个文件中的字节数据我们是看不懂的
3.ObjectInputStream的介绍和使用【重点】
-
构造方法
public ObjectInputStream(InputStream in);
-
反序列操作(正常演示)
public class TestObjectInputStreamDemo { public static void main(String[] args) throws Exception { //1.创建ObjectInputStream对象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dog.txt")); //2.读对象 Dog obj = (Dog) ois.readObject(); System.out.println(obj); //3.释放资源 ois.close(); } }
4.反序列化操作的两种异常演示【了解】
a.ClassNotFoundException 没有找到类异常
出现原因:
序列化之后,反序列化之前,删除了原来序列化的那个类!!
b.InvalidClassException 无效类异常
出现原因:
序列化之后,反序列化之前,修改了原来序列化的那个类!!!
c.实际上序列化流判断类是否有效通过:serialVersionUID来识别的
5.练习:如果需要序列化多个对象怎么操作?
注意: 序列化流一个文件只适合序列化一个对象
分析:
a.把你要序列化的多个对象,保存到一个集合对象
b.将这个集合作为对象,序列化到文件中
c.可以从文件中将整个集合反序列化回来
d.遍历集合把里面的对象一一打印出来
/**
* 如何序列化多个对象?
*/
public class TestDemo {
public static void main(String[] args) throws IOException, ClassNotFoundException {
read();
}
//序列化多个对象
public static void write() throws IOException {
// 注意: 序列化流一个文件只适合序列化一个对象
// 分析:
// a.把你要序列化的多个对象,保存到一个集合对象
ArrayList<Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog(1, "旺财", 4));
dogs.add(new Dog(2, "来福", 5));
dogs.add(new Dog(3, "哮天犬", 6));
// b.将这个集合作为对象,序列化到文件中
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dogs.txt"));
oos.writeObject(dogs);
oos.close();
}
public static void read() throws IOException, ClassNotFoundException {
// c.可以从文件中将整个集合反序列化回来
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dogs.txt"));
ArrayList<Dog> dogs = (ArrayList<Dog>) ois.readObject();
ois.close();
// d.遍历集合把里面的对象一一打印出来
for (Dog dog : dogs) {
System.out.println(dog);
}
}
}
第四章 打印流
1.打印流的介绍
a.我们基础班第一天就接触到的System.out.println()/print().实际上就是调用了打印流的方法
b.打印流PrintStream
c.打印流中重写各种数据类型的print和println方法,打印数据时非常方便
4.打印流不会抛出IOException(会抛出别的Exception)
2.PrintStream的构造和常用方法【重点】
构造方法
public PrintStream(String path);
public PrintStream(File file);
public PrintStream(OutputStream out);
成员方法:
public void print(各种类型); //不带换行的打印
public void println(各种类型); //带换行的打印
public class PrintStreamDemo {
public static void main(String[] args) throws Exception {
//1.创建一个打印流
PrintStream ps1 = new PrintStream("p.txt");
// PrintStream ps2 = new PrintStream(new File("p.txt"));
// PrintStream ps3 = new PrintStream(new FileOutputStream("p.txt"));
//2.打印数据,所见即所得
ps1.println('a');
ps1.println(97);
ps1.println(true);
ps1.println(100);
ps1.println("HelloWorld");
//3.释放资源
ps1.close();
}
}
3.修改系统打印流的流向【了解】
public class PrintStreamDemo02 {
public static void main(String[] args) throws Exception {
//1.创建一个打印流
PrintStream ps1 = new PrintStream("p.txt");
//2.系统的输出
System.out.println("Java");
//3.修改系统打印流的流向
System.setOut(ps1);
System.out.println("Java");
System.out.println("Java");
System.out.println("Java");
System.out.println("Java");
System.out.println("Java");
System.out.println("Java");
System.out.println("Java");
System.out.println("Java");
System.out.println("Java");
System.out.println("Java");
}
}
第五章 装饰设计模式
什么是设计模式??
前辈们为了解决一系列问题,给我总结出来的一套方案
1.装饰模式作用
装饰模式指的是在不改变原类, 不使用继承的基础上,动态地扩展一个对象的功能。
2.装饰者设计模式的4个基本步骤
- 装饰类(我们定义的新类)和被装饰类(原类)必须实现相同的接口
- 在装饰类中必须传入被装饰类的引用
- 在装饰类中对需要扩展的方法进行扩展
- 在装饰类中对不需要扩展的方法调用被装饰类中的同名方法
3.代码实现
/**
* 歌星接口
*/
public interface SingStar {
/**
* 唱歌方法
*/
void sing();
/**
* 跳舞方法
*/
void dance();
}
/**
* 被装饰类
*/
public class LiuDeHua implements SingStar{
//唱歌
public void sing(){
System.out.println("啊哈,给我一杯忘情水..");
}
//跳舞
public void dance(){
System.out.println("蹦恰恰蹦恰恰...");
}
}
/**
* LiuDeHua类的装饰类
*/
//1.装饰类(我们定义的新类)和被装饰类(原类)必须实现相同的接口
public class LiuDeHuaWrapper implements SingStar{
//2.在装饰类中必须传入被装饰类的引用
private LiuDeHua ldh;
public LiuDeHuaWrapper(LiuDeHua ldh) {
this.ldh = ldh;
}
@Override
public void sing() {
//3.在装饰类中对需要扩展的方法进行扩展
System.out.println("刘德华在北京鸟巢唱歌..");
ldh.sing();
}
@Override
public void dance() {
//4.在装饰类中对不需要扩展的方法调用被装饰类中的同名方法
ldh.dance();
}
}
public class TestDemo {
public static void main(String[] args) {
//1.创建被装饰类
LiuDeHua ldh = new LiuDeHua();
//2.创建装饰类对象
LiuDeHuaWrapper wrapper = new LiuDeHuaWrapper(ldh);
//3.调用装饰类中方法
wrapper.sing();
wrapper.dance();
}
}
第六章 commons-io工具包
1.commons-io的介绍和下载
commons-io是Apache公司提供简化IO操作的工具包
我们需要把commons-io工具下载下来:
a.解压
b.在我们模块下创建lib文件夹,将解压后的 commons-io.jar复制进来
c.选中commons-io.jar右键选中Add as Libary 添加到本模块中
2.常用API介绍
-
复制文件API
-
复制文件夹API
public class TestCommonsDemo { public static void main(String[] args) throws IOException { //1.IOUtils 适合复制2G大小以下的文件 IOUtils.copy(new FileInputStream("G:\\upload\\1546241961620.png"),new FileOutputStream("copy.png")); //2.IOUtils适合复制2G大小以上的文件 IOUtils.copyLarge(new FileInputStream("G:\\upload\\1546241961620.png"), new FileOutputStream("copy1.png")); //3.FileUtils 复制文件 FileUtils.copyFileToDirectory(new File("G:\\upload\\1546241961620.png"), new File("G:\\uploads")); //4.FileUtils 复制文件夹 FileUtils.copyDirectoryToDirectory(new File("C:\\Users\\Administrator\\Desktop\\temp\\aaa"),new File("G:\\uploads")); } }
总结:
1.缓冲流【重点】
字节缓冲流(BufferedOutputStream和BufferedInputStream),没有特有方法,性能比普通流更高
字符缓冲流(BufferedWriter和BufferedReader),有特有方法,性能比普通流更高
BufferedWriter:
public void newLine();
BufferedReader:
public String readLine();
2.转换流【重点】
转换输出流: 可以指定编码写文件
OutputStreamWriter
public OutputStreamWriter(OutputStream out,String 指定的编码);
转换输入流: 可以指定编码读文件
InputStreamReader
public InputStreamReader(InputStream in,String 指定的编码);
3.序列化流【理解】
序列化流: 写对象
ObjectOutputStream
public void writeObject(对象);//该对象的类必须实现java.io.Serializable接口
反序列化流: 读对象
ObjectInputStream
public Object readObject();
4.打印流【理解】
PrintStream ps = new PrintStream(String path/File file/OutputStream out);
方法:
print(各种数据类型);
println(各种数据类型);
5.装饰设计模式【理解】
步骤:
a.被装饰类和装饰类实现同一个接口
b.装饰类内部必须含有被装饰类的引用
c.在装饰类中对需要装饰的方法进行装饰
d.在装饰类中对不需要装饰的方法调用原对象的方法
6.commons-io【重点】
IOUtils 复制文件(2G以上和2G以下)
FileUtils 复制文件和复制文件夹