3/23day17_缓冲流_转换流_序列化流_打印流_装饰设计模
复习
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){
}
今日内容
- 缓冲流(高效流, 比普通流性能更高)
- 转换流(编码相关的流, 指定编码)'
- 序列化流(操作对象)
- 打印流(System.out.println())
- 设计模式(装饰设计模式, 4个步骤)
- common-io 工具包(简化io代码)
缓冲流
缓冲流的介绍
-
介绍
缓冲流,也叫高效流,是对4个基本的 FileXxx 流的增强(性能增强, 方法基本一样),所以也是4个流
-
缓冲流的基本原理,是在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
缓冲流的分类
-
按照数据类型分类:
缓冲字节输入流:
BufferedInputStream
---> 对普通的字节输入流``InputStream`增强缓冲字节输出流:
BufferedOutputStream
---> 对普通的字节输出流OutputStream
增强缓冲字符输入流:
BufferedReader
---> 对普通的字符输入流Reader
增强缓冲字符输出流:
BufferedWriter
---> 对普通的字符输出流Writer
增强
字节缓冲流的介绍和使用
-
字节缓冲流的构造
public BufferedInputStream(InputStream in)
:创建一个 新的缓冲输入流。
public BufferedOutputStream(OutputStream out)
: 创建一个新的缓冲输出流。 -
字节缓冲流的使用代码
(使用一次读取一个字节数组的方式,配合字节缓冲流使用速度更快)
public static void copy01() throws Exception{ BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.txt")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("2.txt")); long start = System.currentTimeMillis(); int b = 0; while ((b = bis.read()) != -1) { bos.write(b); } long end = System.currentTimeMillis(); bis.close(); bos.close(); System.out.println(end-start); }
字符缓冲流的介绍和使用
-
字符缓冲流的构造
public BufferedReader(Reader in)
:创建一个 新的缓冲输入流。
public BufferedWriter(Writer out)
: 创建一个新的缓冲输出流。 -
字符缓冲流的特有方法
BufferedRead
:public String readLine()
: 读一行文字(只要没到换行)。(读不到会返回null)
BufferedWriter
:public void newLine()
: 写一行行分隔符(换行符),由系统属性定义符号(具有跨平台性)。-
BufferedRead的一次读取一行使用
public static void bwread() throws IOException{ BufferedReader br = new BufferedReader(new FileReader("2.txt")); System.out.println(br.readLine()); //===========一次读取一行的标准写法========== String line=""; while ((line = br.readLine()) != null) { System.out.println(line); } br.close(); }
缓冲流练习
-
public static void main(String[] args) throws IOException {
ArrayList<String> list = new ArrayList<>();
BufferedReader br = new BufferedReader(new FileReader("1.txt"));
String line ="";
while ((line = br.readLine()) != null) {
list.add(line);
}
br.close();
/* Collections.sort(list, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.charAt(0)-o1.charAt(0);
}
});*/
Collections.sort(list,((o1, o2) -> o1.charAt(0)-o2.charAt(0)));
/*for (String s : list) {
System.out.println(s);
}*/
BufferedWriter bw = new BufferedWriter(new FileWriter("2"));
for (String s : list) {
bw.write(s);
bw.newLine();
}
bw.close();
}
转换流
编码和解码
-
编码: 把字符按照某种规则,将字符转到字节(二进制)存储到计算机中,称为编码 。
-
解码: 将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码 。
字符集
字符集 Charset :也叫编码表。是一个系统支持的所有字符的集合,包括各国家文字、标点符号、图形符号、数字等。
字符编码
字符编码 Character Encoding : 就是一套自然语言的字符与二进制数之间的对应规则。
常见的字符集和字符编码
ASCII 字符集 --> ASCII 编码 , 规定了ASCII字符集中所有的字符都占1个字节(0-127)用于显示现代英语,主要包括控制字符(回车键、退格、换行键等)和可显
示字符(英文大小写字符、阿拉伯数字和西文符号)。
GBK字符集 ---> GBK编码, 规定所有的中文字符都占2个字节(这2个字节都是负数).是为了显示中文而设计的一套字符集。
Unicode字符集 ---> UTF-8编码, 规定所有中文字符都占3个字节,Unicode编码系统为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。
ISO-8859-1字符集 ---> 用于显示欧洲使用的语言
编码引出的问题
IDEA默认使用UTF-8编码, windows默认使用GBK编码
public class ReaderDemo {
public static void main(String[] args) throws IOException {
FileReader fileReader = new FileReader("E:\\File_GBK.txt");
int read;
while ((read = fileReader.read()) != -1) {
System.out.print((char)read);
}
fileReader.close();
}
}
输出结果:
���
使用转换流InputStreamReader
类解决读取中文的问题
转换流 java.io.InputStreamReader ,是Reader的子类,是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
-
构造方法
InputStreamReader(InputStream in)
: 创建一个使用默认字符集的字符流。(使用IDEA默认编码)
InputStreamReader(InputStream in, String charsetName)
: 创建一个指定字符集的字符流。(第二个参数为指定使用何种编码读取文件)
使用转换流OutputStreamReader
类写不同编码的中文
转换流 java.io.OutputStreamWriter ,是Writer的子类,是从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
-
构造方法
OutputStreamWriter(OutputStream in)
: 创建一个使用默认字符集的字符流。
OutputStreamWriter(OutputStream in, String charsetName)
: 创建一个指定字符集的字符流。 -
按指定编码输出
public static void main(String[] args) throws Exception { OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("1.txt"), "UTF-8"); osw.write("你好"); osw.close(); }
转换流的理解
转换流代码案例
public static void main(String[] args) throws Exception{
InputStreamReader isr = new InputStreamReader(new FileInputStream("GBK.txt"), "GBK");
OutputStreamWriter ops = new OutputStreamWriter(new FileOutputStream("UTF-8.TXT"), "UTF-8");
int ch=0 ;
while ((ch = isr.read()) != -1) {
ops.write((char)ch);
}
isr.close();
ops.close();
}
序列化
序列化流概述
-
序列化流: 写出对象的流
ObjectOutputStream(java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。)
-
反序列化流: 读取对象的流
ObjectInputStream(ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。)
ObjectOutputStream类的介绍和使用
-
构造方法
public ObjectOutputStream(OutputStream out)
: 创建一个指定OutputStream的ObjectOutputStream。 -
序列化操作的前提
想要序列化, 必须将需要序列化的对象类实现Serializable(可序列化)接口
Serializable接口没有需要重写的方法, 这种接口被称为标记接口
-
序列化操作代码演示
public static void main(String[] args) throws Exception{ ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dog.txt")); oos.writeObject(new Dog(20,"小狗狗")); oos.close(); }
写出的dog.txt文件中是字节文件.
ObjectInputStream类的介绍和使用
-
构造方法
public ObjectInputStream(InputStream in)
: 创建一个指定InputStream的ObjectInputStream。 -
反序列化操作代码演示
public static void main(String[] args) throws Exception{ ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dog.txt")); Object o = ois.readObject(); System.out.println(o); ois.close(); }
反序列化可能会出现的异常
-
对于JVM可以反序列化对象,它必须是能够找到class文件的类。如果找不到该类的class文件,则抛出一个
ClassNotFoundException
异常。原因: 找不到序列化时的对象类
-
当JVM反序列化对象时,能找到class文件,但是class文件在序列化对象之后发生了修改,那么反序列化操作也会失败,抛出一个
InvalidClassException
异常。原因: 修改了序列化的对象类的内容,根据版本号识别
-
Serializable 接口给需要序列化的类,提供了一个序列版本号。 serialVersionUID 该版本号的目的在于验证序列化的对象和对应类是否版本匹配。(允许程序员自己管理版本号代码如下)
public class Employee implements java.io.Serializable { // 加入序列版本号 private static final long serialVersionUID = 1L; public String name; public String address; // 添加新的属性 ,重新编译, 可以反序列化,该属性赋为默认值. public int eid; public void addressCheck() { System.out.println("Address check : " + name + " -- " + address); } }
序列化多个对象
-
注意: 序列化流一个文件只适合序列化一个对象(如果一个文件序列化多个对象, 会缺少标记)
-
操作步骤:
- 把序列化的多个对象,保存到一个集合对象(集合已经实现了Serializable接口)
- 把这个集合作为对象,序列化到文件中(其实就是将集合容器作为对象序列化到文件中)
-
序列化多个对象代码演示:
public class TestDemo { public static void main(String[] args) throws Exception { write(); read(); } public static void write() throws IOException { ArrayList<Dog> dogs = new ArrayList<>(); dogs.add(new Dog(23, "dahua")); dogs.add(new Dog(11, "erhua")); dogs.add(new Dog(55, "sanhua")); ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dogs.txt")); oos.writeObject(dogs); oos.close(); } public static void read() throws Exception { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dogs.txt")); Object o = ois.readObject(); //反序列化文件, 返回值是Object对象, 需要强转成集合(泛型用父类多态接收) for (Object dog : (ArrayList<Object>)o) { System.out.println(dog); } ois.close(); } }
打印流(PrintStream)
打印流的介绍
System.out.println()
在控制台打印输出,是调用print
方法和 println
方法完成的,这两个方法都来自于java.io.PrintStream
类(成为打印流),该类能够方便地打印各种数据类型的值(打印流中重写了各种数据类型的print
和printlin
方法),是一种便捷的输出方式。
打印流的用法
-
构造方法
public PrintStream(String fileName)
: 使用指定的文件名创建一个新的打印流。public PrintStream(File file)
: 直接指定file对象public PrintStream(OutputStream out)
: 输出流绑定的哪个对象, 就打印到哪个对象中 -
成员方法
public void print(各种数据类型);
public void println(各种数据类型);
-
打印流代码演示
public static void main(String[] args) throws Exception{ PrintStream ps1 = new PrintStream("p.txt"); PrintStream ps2 = new PrintStream(new File("p1.txt")); PrintStream ps3 = new PrintStream(new FileOutputStream("p2.txt")); ps1.print("随便写各种类型数据"); }
-
修改系统打印流的流向
public static void main(String[] args) throws Exception{ PrintStream ps = new PrintStream("p.txt"); ps.print("更改打印"); System.out.println("JAVA"); //相当于修改了System静态变量out的值 System.setOut(ps); System.out.println(); System.out.println("java更改"); }
装饰设计模式
设计模式是指, 前辈们为了解决一系列问题设计的方案
在我们今天所学的缓冲流中涉及到java的一种设计模式,叫做装饰模式.
装饰设计模式概述(作用)
指在不改变原类, 不适用继承的基础上, 动态地扩展一个对象的功能.
装饰设计模式的4个基本步骤
- 装饰类(需要装饰的新类)和被装饰类(原类)必须实现相同的接口(可以将原类中的成员方法抽出来, 放在接口中)
- 在装饰类中必须传入被装饰类的引用(就是在新类中定义装饰类的成员变量对象)
- 在装饰类中对需要扩展的方法进行扩展
- 在装饰类中对不需要扩展的方法调用被装饰类中的同名方法
装饰设计模式代码演示
-
先提供方法接口
public interface Star { public void sing(); public void dance(); }
-
让原类去实现接口
public class LiuDeHua implements Star { @Override public void sing() { System.out.println("刘德华在唱忘情水..."); } @Override public void dance() { System.out.println("刘德华在跳街舞..."); } }
-
写装饰类,在装饰类中扩展
/* 装饰模式遵循原则: 装饰类和被装饰类必须实现相同的接口 在装饰类中必须传入被装饰类的引用 在装饰类中对需要扩展的方法进行扩展 在装饰类中对不需要扩展的方法调用被装饰类中的同名方法 */ public class LiuDeHuaWarpper implements Star { // 存放被装饰类的引用 private LiuDeHua liuDeHua; // 通过构造器传入被装饰类对象 public LiuDeHuaWarpper(LiuDeHua liuDeHua){ this.liuDeHua = liuDeHua; } @Override public void sing() { // 对需要扩展的方法进行扩展增强 System.out.println("刘德华在鸟巢的舞台上演唱忘情水."); } @Override public void dance() { // 不需要增强的方法调用被装饰类中的同名方法 liuDeHua.dance(); } }
commons-io工具包
commons-io工具包概述
commons-io是apache开源基金组织提供的一组有关IO操作的类库,可以挺提高IO功能开发的效率。commons-io工具包提供了很多有关io操作的类,见下表:
包 | 功能描述 |
---|---|
org.apache.commons.io | 有关Streams、Readers、Writers、Files的工具类 |
org.apache.commons.io.input | 输入流相关的实现类,包含Reader和InputStream |
org.apache.commons.io.output | 输出流相关的实现类,包含Writer和OutputStream |
org.apache.commons.io.serialization | 序列化相关的类 |
commons-io工具包 使用步骤
步骤:
- 下载commons-io相关jar包;http://commons.apache.org/proper/commons-io/
- 把commons-io-2.6.jar包复制到指定的Module的lib目录中(必须在指定的Module的lib目录下, lib目录和src目录为同级目录)
- 将commons-io-2.6.jar加入到classpath中(IDEA中右键该工具包选择Add as Libary表示添加到本模块中作为工具库)
commons-io工具包常用API
-
commons-io提供了一个工具类 org.apache.commons.io.IOUtils,封装了大量IO读写操作的代码。其中有两个常用方法:
- public static int copy(InputStream in, OutputStream out); 把input输入流中的内容拷贝到output输
出流中,返回拷贝的字节个数(适合文件大小为2GB以下) - public static long copyLarge(InputStream in, OutputStream out);把input输入流中的内容拷贝到
output输出流中,返回拷贝的字节个数(适合文件大小为2GB以上)
代码演示
public static void main(String[] args) throws Exception { // 文件路径需要修改,改成自己文件的路径 File file = new File("src/test.txt"); FileInputStream is = new FileInputStream(file); // 文件路径需要修改 File file1 = new File("src/test1.txt"); FileOutputStream os = new FileOutputStream(file1); // 文件复制 IOUtils.copy(is, os); }
- public static int copy(InputStream in, OutputStream out); 把input输入流中的内容拷贝到output输
-
commons-io还提供了一个工具类org.apache.commons.io.FileUtils,封装了一些对文件操作的方法:
- public static void copyFileToDirectory(final File srcFile, final File destFile) //复制文件到另外一个目录
下。 - public static void copyDirectoryToDirectory( file1 , file2 );//复制file1目录到file2位置。
代码演示
public static void main(String[] args) throws IOException { //1.将d:\\视频.itcast文件复制到e:\\下 FileUtils.copyFileToDirectory(new File("d:\\视频.itcast"), new File("e:\\")); //2.将"d:\\多级目录"复制到"e:\\"下。 FileUtils.copyDirectoryToDirectory(new File("d:\\多级目录"), new File("e:\\")); }
- public static void copyFileToDirectory(final File srcFile, final File destFile) //复制文件到另外一个目录
今日小结
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 复制文件和复制文件夹