day11【Properties类、缓冲流、转换流、序列化流、装饰者模式、commons-io工具包】
昨天内容:
FileOutputStream: 文件的字节输出流
public void close();
public void flush();
public void write(int b);
public void write(byte[] bs);
public void write(byte[] bs,int off,int len);
FileInputStream: 文件的字节输入流
public void close();
public int read();
public int read(byte[] bs);
FileWriter: 文件的字符输出流
public void close();
public void flush();
public void write(int ch);
public void write(char[] chs);
public void write(char[] chs,int off,int len);
public void write(String str);
public void write(String str,int off,int len);
FileReader: 文件的字符输入流
public void close();
public int read();
public int read(char[] chs);
今日内容:
*Properties类(后期作为框架的配置文件)
缓冲流
转换流
序列化流
打印流
commons-io框架(工具包)
装饰设计模式
一.IO流的异常处理
1.JDK7之前的标准IO处理
/**
* JDK1.7之前的处理
*/
public static void method01() {
//1.创建流
FileReader fr = null;
try {
fr = new FileReader("1.txt");
//2.读数据
int ch = fr.read();
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.释放资源
try {
if (fr != null) {
fr.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
2.JDK7引入的IO处理
/**
* JDK1.7以及之后的处理
* try-with-resource
*/
public static void method02() {
try (FileReader fr = new FileReader("1.txt");其他资源) {
int ch = fr.read();
} catch (IOException e) {
e.printStackTrace();
}
}
二.Properties类
1.Properties类的介绍
Properties 类表示一个持久的属性集
集: 它是一个集合
属性集: 就是一个键值对的集合(本质就是一个Map,但是属性名和属性值都是String类型)
持久的: 该属性集可以直接和磁盘交互(该类含有直接写数据和读数据的方法)
2.构造方法
public Properties(); -- 创建一个空的属性集合
public class PropertiesDemo {
public static void main(String[] args) {
//1.创建一个持久的属性集
Properties ps = new Properties();
System.out.println(ps);
}
}
3.基本保存数据的方法
public Object setProperty(String key,String value); -- 相当于put方法
public Set<String> stringPropertyNames(); -- 相当于keySet方法
public String getProperty(String propertyName); -- 相当于get方法
public class PropertiesDemo {
public static void main(String[] args) {
//1.创建一个持久的属性集
Properties ps = new Properties();
System.out.println(ps);
//2.添加数据
ps.setProperty("xiaomi","1999");
ps.setProperty("huawei","2999");
ps.setProperty("sanxing","3999");
ps.setProperty("apple", "4999");
//3.遍历,stringPropertyNames
Set<String> propertyNames = ps.stringPropertyNames();
System.out.println(propertyNames);
//4.循环
for (String propertyName : propertyNames) {
String propertyValue = ps.getProperty(propertyName);
System.out.println(propertyName+"="+propertyValue);
}
}
}
4.持久化的方法
public void store(OutputStream/Writer,String comments); -- 将Properties中的数据保存到磁盘
//5.保存ps中数据
FileOutputStream fos = new FileOutputStream("1.properties");
ps.store(fos,"this is a test file");
fos.close();
public void load(InputStream/Reader); -- 将文件中的数据,加载到Properties对象中
//6.从文件中加载数据
System.out.println("=============");
Properties ps1 = new Properties();
System.out.println(ps1);
FileInputStream fis = new FileInputStream("1.properties");
ps1.load(fis);
fis.close();
System.out.println(ps1);
注意:
a.规范,如果是Properties保存数据的文件,建议以.properties为后缀
b.Properties的load方法,只能读取符合Properties保存的那种文件格式
三.缓冲流
1.缓冲流的作用
缓冲流也称为高效流,是普通流的增强(性能方法)
2.缓冲(Buffered)流的分类
字节输入流 --> 缓冲字节输入流:BufferedInputStream
字节输出流 --> 缓冲字节输出流:BufferedOutputStream
字符输入流 --> 缓冲字符输入流:BufferedReader
字符输出流 --> 缓冲字符输出流:BufferedWriter
所以,在创建缓冲流时,构造方法需要传入普通流对象
3.字节缓冲流的介绍和使用
-
字节缓冲流的构造
public BufferedInputStream(InputStream in); -- 创建字节缓冲输入流,需要传入普通的字节输入流 public BufferedOutputStream(OutputStream out); -- 创建字节缓冲输出流,需要传入普通的字节输出流
-
字符缓冲流的高效测试
public class BufferedDemo01 { public static void main(String[] args) throws IOException { long start = System.currentTimeMillis(); copy02(); long end = System.currentTimeMillis(); System.out.println("耗时:" + (end - start) + "毫秒"); } /** * 复制文件:使用缓冲流 * 耗时:170毫秒 */ public static void copy02()throws IOException { //1.创建两个缓冲流 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.png")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy02.png")); //2.复制文件 int b = 0; while ((b = bis.read()) != -1) { bos.write(b); } //2.释放资源 bos.close(); bis.close(); } /** * 复制文件:使用普通流 * 耗时:33261毫秒 */ public static void copy01() throws IOException { //1.创建两个流 FileInputStream fis = new FileInputStream("1.png"); FileOutputStream fos = new FileOutputStream("copy.png"); //2.复制(单个字节,字节数组) int b = 0; while ((b = fis.read()) != -1) { fos.write(b); } //3.释放资源 fos.close(); fis.close(); } //如果使用缓冲流+数组复制,效果更佳 /** * 复制文件:使用缓冲流,再加上数组复制 * 耗时:13毫秒 */ public static void copy03()throws IOException { //1.创建两个缓冲流 BufferedInputStream bis = new BufferedInputStream(new FileInputStream("1.png")); BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("copy02.png")); //2.复制文件 byte[] bs = new byte[1024 * 100]; int len = 0; while ((len = bis.read(bs)) != -1) { bos.write(bs, 0, len); } //2.释放资源 bos.close(); bis.close(); } }
4.字符缓冲流的介绍和使用
-
a.字符缓冲流的构造
public BufferedReader(Reader r); -- 创建缓冲字符输入流,需要传入普通的字符输入流 public BufferedWriter(Writer w); -- 创建缓冲字符输出流,需要传入普通的字符输出流
-
b.字符缓冲流的2个特有方法
BufferedReader 缓冲字符输入流,特有方法 public String readLine(); -- 一次读取一行 /** * 字符缓冲输入流:BufferedReader的特有方法 */ public static void read()throws IOException { //1.创建BufferedReader对象 BufferedReader br = new BufferedReader(new FileReader("1.txt")); //2.读数据 //String line = br.readLine(); //System.out.println(line); //==========一次读取一行的标准循环代码=========== String line = null; //保存每次读到的一行数据 while ((line = br.readLine()) != null) { System.out.println(line); } //3.释放资源 br.close(); } BufferedWriter 缓冲字符输入流,特有方法 public void newLine(); -- 写入一个跨平台的换行符 /** * 字符缓冲输出流:BufferedWriter的特有方法 */ public static void write() throws IOException { //1.创建BufferedWriter对象 BufferedWriter bw = new BufferedWriter(new FileWriter("1.txt")); //2.写数据 for (int i = 0; i < 10; i++) { bw.write("php"); //写一个跨平台的换行 bw.newLine(); } //3.释放资源 bw.close(); }
5.综合练习:文本排序
需求:将文本文件中的内容读出,并按照序号排序,最后写入到一个新的文本文件中
/**
* 文本排序
*/
public class BufferedDemo03 {
public static void main(String[] args) throws IOException {
ArrayList<String> arr = new ArrayList<String>();
//1.读文件
BufferedReader br = new BufferedReader(new FileReader("csb.txt"));
String str = null;
while ((str = br.readLine()) != null) {
arr.add(str);
}
br.close();
//2.排序
Collections.sort(arr, (o1, o2) -> o1.charAt(0) - o2.charAt(0));
//3.写文件
BufferedWriter bw = new BufferedWriter(new FileWriter("new.txt"));
for (String line : arr) {
bw.write(line);
//写一个跨平台的换行
bw.newLine();
}
bw.close();
}
}
四.转换流
1.字符编码(编码和解码)
一套规则,每个字符和对应的码值之间转换的规则
编码: 将字符 转成 对应的码值
解码: 将码值 转成 对应的字符
2.字符集
是一个系统支持的所有字符的集合(包括文字,标点,图形)
常见的字符集和字符编码:
ASCII字符集,包含美国国家的字符,符号,图形等
其对应的字符编码,称为ASCII编码,在ASCII编码中所有的字符只占一个字节
GBxxxx字符集:
GB2312字符集,包含常见的中文(大概7000多个)
GBK字符集, 包含大部分的中文以及日韩文字以及繁体文字(大概20000多个)
GB18030字符集,包含基本上所有的中文,生僻字等(大概70000多个)
GBXxxx字符集,对应的字符编码:GBK编码,在GBK编码中一个中文占2个字节
Unicode字符集(万国字符集):
基本上包含了全球所有国家的文字,符号等
该字符集对应的编码: Unicode编码.UTF-8编码,UTF-16编码,UTF-32编码
在UTF-8编码中,一个中文占3个字节
========================================================
= "总结:对于中国人来说: =
= ASCII编码,一个英文占一个字节 =
= GBK编码,一个中文占2个字节 =
= UTF-8编码,一个中文占3个字节 =
========================================================
ISO-8859-1字符集:
拉丁字符集(西欧国家的字符集)
其对应的编码称为ISO-8859-1编码
因为我们以后的服务器Tomcat7默认使用ISO-8859-1编码
3.编码引出的问题
IDEA读取中文默认使用UTF-8编码
windows默认使用GBK编码
假设windows上有一个文件:gbk.txt(GBK编码),内容是:你好
使用IDEA默认UTF-8编码去读:
public class GBKAndUTF8Demo {
public static void main(String[] args) throws IOException {
//1.windows中一个文件gbk.txt,默认使用GBK编码,一个中文2个字节
//2.IDEA默认使用Utf-8编码,一个中文3个字节
//3.这时如果用IDEA去读取gbk.txt文件就会出现乱码
FileReader fr = new FileReader("gbk.txt");
int ch = fr.read();
System.out.println((char)ch);
fr.close();
}
}
结果肯定会出现乱码!!!!
如何解决这个乱码问题:
1.强制要求用户的文件,必须使用UTF-8(使用Notepad++打开,选择UTF-8无BOM编码)
2.可以修改IDEA默认编码,将编码改为GBK
3.能不能通过代码指定编码,在读取文件中同时指定使用何种编码????
可以!! 使用转换流!!!转换流和普通流区别就在于可以自由指定编码
4.使用转换流InputStreamReader解决中文问题
转换输入流:
InputStreamReader extends Reader
-
构造方法
public InputStreamReader(InputStream in); -- 创建转换输入流,传入普通的字节输入流用于指定文件 public InputStreamReader(InputStream in,String charsetName); -- 创建转换入流,指定文件,指定读取时使用何种编码
-
使用InputStreamReader读取不同编码的文件(代码演示)
public class GBKAndUTF8Demo02 { public static void main(String[] args) throws IOException { //1.创建转换输入流 // InputStreamReader isr = new InputStreamReader(new FileInputStream("gbk.txt"),"GBK"); // 指定编码GBK InputStreamReader isr = new InputStreamReader(new FileInputStream("utf8.txt"),"UTF-8"); // 指定编码UTF-8 //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); -- 创建转换输出流,指定输出的目的地,默认UTF-8编码 public OutputStreamWriter(OutputStream out,String charsetName);-- 创建转换输出流,指定输出的目的地,同时指定输出的编码
-
输出指定编码的中文
public class GBKAndUTF8Demo03 { public static void main(String[] args) throws IOException { //1.创建转换输出流 OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("newUTF.txt"),"UTF-8"); OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("newGBK.txt"),"GBK"); //2.写数据 osw.write("你好"); //3.释放资源 osw.close(); } }
6.练习:转换文件编码
将GBK编码的文本文件,转换为UTF-8编码的文本文件
a.先指定GBK编码,读出GBK文件
b.再指定UTF_8编码,写出UTF-8文件即可
c.循环
public class GBKAndUTF8Demo04 {
public static void main(String[] args) throws IOException {
//1.创建转换输入流,用于读GBK文件
InputStreamReader isr = new InputStreamReader(new FileInputStream("GBK.txt"), "GBK");
//2.创建输出流,用于写UTF-8文件
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("UTF8.txt"), "UTF-8");
//3.复制文件
int ch = 0;
while ((ch = isr.read()) != -1) {
osw.write(ch);
}
//4.释放资源
osw.close();
isr.close();
}
}
五.序列化流
1.什么是序列化流
序列化流以对象为单位来操作的
序列化流: 也称为对象的输出流,写对象,ObjectOutputStream
反序列化流: 也称为对象的输入流,读对象,ObjectInputStream
2.ObjectOutputStream的介绍和使用
序列化流,写对象
-
构造方法
public ObjectOutputStream(OutputStream out); -- 创建序列化流,传入普通字节输出流
-
序列化操作的前提
被序列化的类必须使用java.io.Serializable接口,以启用其序列化功能 该接口没有任何方法,是一个标记接口
-
序列化操作(代码演示)
public class ObjectOutputStreamDemo { public static void main(String[] args) throws IOException { //1.创建一个序列化流对象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("2.txt")); //2.写对象 Dog dd = new Dog(2, "旺财"); //NotSerializableException oos.writeObject(dd); //3.释放资源 oos.close(); } }
3.ObjectInputStream的介绍和使用
反序列化流,读对象
-
构造方法
public ObjectInputStream(InputStream in);
-
反序列操作(正常演示)
public class ObjectInputStreamDemo { public static void main(String[] args) throws Exception { //1.创建一个反序列化流 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("2.txt")); //2.读对象 Object obj = ois.readObject(); System.out.println(obj); //3.释放资源 ois.close(); } } 注意: a.反序列流只能读序列化流写成的文件 b.readObject返回的是Object类型,具体是什么类型由序列化流写入时对象的类型决定的
-
反序列化操作的两种错误演示
a.ClassNotFoundException 类没有找到异常 原因: 序列化之后,反序列号之前,将类删除 b.InvalidClassException 无效类异常 原因: 序列化之后,反序列号之前,将类进行的修改,这时类就无效了 扩展1: 类是否有效,通过一个序列化版本号来判定的,序列化版本号通过类的内容计算而来 扩展2: Java允许程序员自己管理序列化版本号,只需要在类中定义一个变量,即可,变量必须是以下格式 public static final long serialVersionUID = 值; 版本号是否修改,完全由程序员自己控制 扩展3: 瞬态关键字transient transient用于修饰类的成员变量,被transient修饰的成员变量和普通成员变量没有任何区别!! 被transient修饰的成员变量,在序列化时会自动忽略
4.练习:如果需要序列化多个对象怎么操作?
注意: 序列化流写多个对象是可以的
反序列化读多个对象时,读不到对象直接抛出异常
所以我们不想一次写多个对象,一次读多个对象
解决方案: 创建一个集合,添加多个对象,然后操作该集合即可
/**
* 读写多个对象
*/
public class ObjectStreamDemo {
public static void main(String[] args) throws Exception {
readObjects();
}
/**
* 读集合
*/
public static void readObjects() throws Exception {
//1.创建一个反序列化流
ObjectInputStream ois = new ObjectInputStream(new FileInputStream("dogs.txt"));
//2.读对象
ArrayList<Dog> arr = (ArrayList<Dog>) ois.readObject();
for (Dog dog : arr) {
System.out.println(dog);
}
//3.释放资源
ois.close();
}
/**
* 写集合
*/
public static void writeObjects() throws IOException {
//1.创建一个集合,添加多个对象,然后操作该集合即可
ArrayList<Dog> dogs = new ArrayList<Dog>();
dogs.add(new Dog(1, "旺小财"));
dogs.add(new Dog(2, "旺中财"));
dogs.add(new Dog(3, "旺大财"));
dogs.add(new Dog(4, "旺老财"));
dogs.add(new Dog(5, "旺死财"));
//2.将集合作为一个对象
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("dogs.txt"));
//3.写对象
oos.writeObject(dogs);
//4.释放资源
oos.close();
}
}
六.打印流
1.打印流PrintStream的介绍
打印流可以快速打印各种数据类型,并且打印流不会抛出IOException异常
特点:
a.所见即所得
b.不会抛出IOException异常
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("ps.txt");
// PrintStream ps2 = new PrintStream(new File("ps.txt"));
// PrintStream ps3 = new PrintStream(new FileOutputStream("ps.txt"));
//2.打印各种数据类型(所见即所得)
// ps1.print(97);
// ps1.print('a');
// ps1.print(3.14);
// ps1.print(true);
// ps1.print("HelloWorld");
ps1.println(97);
ps1.println('a');
ps1.println(3.14);
ps1.println(true);
ps1.println("HelloWorld");
//3.释放资源
ps1.close();
}
}
3.扩展_修改打印流的流向(了解)
public class PrintStreamDemo02 {
public static void main(String[] args) throws Exception {
System.out.println("HelloWorld");
// PrintStream ps = System.out;
// ps.println("Java");
// ps.println("PHP");
// ps.println("ISO");
//修改打印流的流向
PrintStream ps = new PrintStream("ps.txt");
// System.out = ps;
System.setOut(ps);
//这些内容不会打印到控制台
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
System.out.println("HelloWorld");
}
}
第七章 装饰设计模式
1.装饰模式作用
可以在不改变原有类,不是用继承的基础上对一个类的方法进行增强
2.装饰者设计模式的4个基本步骤
- 装饰类和被装饰类必须实现相同的接口
- 在装饰类中必须传入被装饰类的引用
- 在装饰类中对需要扩展的方法进行扩展
- 在装饰类中对不需要扩展的方法调用被装饰类中的同名方法
3.代码实现
/**
* 装饰类和被装饰类需要实现的接口
*/
public interface SingerInterface {
/**
* 唱歌方法
*/
void sing();
/**
* 跳舞方法
*/
void dance();
}
/**
* 原有类:被装饰的类
*/
public class LiuDeHua implements SingerInterface{
@Override
public void sing() {
System.out.println("啊哈,给我一杯忘情水~~~");
}
@Override
public void dance() {
System.out.println("蹦恰恰蹦恰恰蹦恰恰~~~");
}
}
/**
* 装饰类
*/
public class LiuDeHuaWapper implements SingerInterface{
/**
* LiuDeHua对象
*/
private LiuDeHua liuDeHua;
public LiuDeHuaWapper(LiuDeHua liuDeHua) {
this.liuDeHua = liuDeHua;
}
@Override
public void sing() {
System.out.println("喝口水,润喉...");
liuDeHua.sing();
System.out.println("吃个糖,宝喉...");
}
@Override
public void dance() {
liuDeHua.dance();
}
}
public class TestDemo {
public static void main(String[] args) {
//1.创建一个刘德华对象
LiuDeHua liuDeHua = new LiuDeHua();
//2.创建一个刘德华装饰类对象
LiuDeHuaWapper wapper = new LiuDeHuaWapper(liuDeHua);
//3.调用装饰类的方法
wapper.sing();
wapper.dance();
}
}
第八章 commons-io工具包
1.commons-io的介绍和下载
commons-io工具包,专门用于封装各种IO操作,由第三方公司(Apache)开发
下载地址:http://commons.apache.org/proper/commons-io/
解压后我们只需要用到:commons-io.jar(字节码文件压缩包)
2.常用API介绍
-
复制文件API(IOUtils工具类)
public static int copy(InputStream in, OutputStream out); 复制小文件(2GB以下) public static long copyLarge(InputStream in, OutputStream out);复制大文件(2GB以上) public class CommonsIODemo { public static void main(String[] args) throws IOException { //1.复制文件 IOUtils.copy(new FileInputStream("222.png"),new FileOutputStream("copy.png")); IOUtils.copyLarge(new FileInputStream("222.png"),new FileOutputStream("copy22.png")); } }
-
复制文件到某个文件夹下(FileUtils工具类)
public static void copyFileToDirectory(File srcFile, File destFile); public class CommonsIODemo { public static void main(String[] args) throws IOException { //2.复制文件到某个文件夹下 FileUtils.copyFileToDirectory(new File("222.png"),new File("K:\\其他")); } }
-
复制文件夹API(FileUtils工具类)
public static void copyDirectoryToDirectory(File file1,File file2); public class CommonsIODemo { public static void main(String[] args) throws IOException { //3.复制文件夹到某个文件夹下 FileUtils.copyDirectoryToDirectory(new File("E:\\heima126\\day11"),new File("K:\\其他")); } }
总结
"能够使用Properties的load方法加载文件中配置信息 能够使用字节缓冲流读取数据到程序 能够使用字节缓冲流写出数据到文件 能够明确字符缓冲流的作用和基本用法 "能够使用缓冲流的特殊功能 能够阐述编码表的意义 "能够使用转换流读取指定编码的文本文件 "能够使用转换流写入指定编码的文本文件 "能够使用序列化流写出对象到文件 "能够使用反序列化流读取文件到程序中 "能够理解装饰模式的实现步骤 "能够使用commons-io工具包