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 複製文件和複製文件夾