javaIO学习下:javase学习(三)
壓縮(ZIP文檔)
Java IO類庫是支持讀寫壓縮格式的數據流的。我們可以把一個或一批文件壓縮成一個zip文檔。這些壓縮相關的流類是按字節處理的。先看下設計壓縮解壓縮的相關流類。
| CheckedInputStream | getCheckSum()可以為任何InputStream產生校驗和(不僅是解壓縮) |
| CheckedOutputStream | getCheckSum()可以為任何OutputStream產生校驗和(不僅是壓縮) |
| DeflaterOutputStream | 壓縮類的基類 |
| ZipOutputStream | 繼承自DeflaterOutputStream,將數據壓縮成Zip文件格式 |
| GZIPOutputStream | 繼承自DeflaterOutputStream,將數據壓縮成GZIP文件格式 |
| InflaterInputStream | 解壓縮類的基類 |
| ZipInputStream | 繼承自InflaterInputStream,解壓縮Zip文件格式的數據 |
| GZIPInputStream | 繼承自InflaterInputStream,解壓縮GZIP文件格式的數據 |
表格中CheckedInputStream ?和 CheckedOutputStream ?一般會和Zip壓縮解壓過程配合使用,主要是為了保證我們壓縮和解壓過程數據包的正確性,得到的是中間沒有被篡改過的數據。
我們以CheckedInputStream ?為例,它的構造器需要傳入一個Checksum類型:
1????public?CheckedInputStream(InputStream?in,?Checksum?cksum)?{ 2????????super(in); 3????????this.cksum?=?cksum; 4????}而Checksum 是一個接口,可以看到這里又用到了策略模式,具體的校驗算法是可以選擇的。Java類庫給我提供了兩種校驗和算法:Adler32 和 CRC32,性能方面可能Adler32 會更好一些,不過CRC32可能更準確。各有優劣吧。
好了,接下來看下壓縮/解壓縮流的具體使用。
將多個文件壓縮成zip包
1public?class?ZipFileUtils?{2????public?static?void?compressFiles(File[]?files,?String?zipPath)?throws?IOException?{34????????//?定義文件輸出流,表明是要壓縮成zip文件的5????????FileOutputStream?f?=?new?FileOutputStream(zipPath);67????????//?給輸出流增加校驗功能8????????CheckedOutputStream?checkedOs?=?new?CheckedOutputStream(f,new?Adler32());9 10????????//?定義zip格式的輸出流,這里要明白一直在使用裝飾器模式在給流添加功能 11????????//?ZipOutputStream?也是從FilterOutputStream?繼承下來的 12????????ZipOutputStream?zipOut?=?new?ZipOutputStream(checkedOs); 13 14????????//?增加緩沖功能,提高性能 15????????BufferedOutputStream?buffOut?=?new?BufferedOutputStream(zipOut); 16 17????????//對于壓縮輸出流我們可以設置個注釋 18????????zipOut.setComment("zip?test"); 19 20????????//?下面就是從Files[]?數組中讀入一批文件,然后寫入zip包的過程 21????????for?(File?file?:?files){ 22 23????????????//?建立讀取文件的緩沖流,同樣是裝飾器模式使用BufferedReader 24????????????//?包裝了FileReader 25????????????BufferedReader?bfReadr?=?new?BufferedReader(new?FileReader(file)); 26 27????????????//?一個文件對象在zip流中用一個ZipEntry表示,使用putNextEntry添加到zip流中 28????????????zipOut.putNextEntry(new?ZipEntry(file.getName())); 29 30????????????int?c; 31????????????while?((c?=?bfReadr.read())?!=?-1){ 32????????????????buffOut.write(c); 33????????????} 34 35????????????//?注意這里要關閉 36????????????bfReadr.close(); 37????????????buffOut.flush(); 38????????} 39????????buffOut.close(); 40????} 41 42????public?static?void?main(String[]?args)?throws?IOException?{ 43????????String?dir?=?"d:"; 44????????String?zipPath?=?"d:/test.zip"; 45????????File[]?files?=?Directory.getLocalFiles(dir,".*\\.txt"); 46????????ZipFileUtils.compressFiles(files,?zipPath); 47????} 48}在main函數中我們使用了本文中?File其實是個工具類?章節里的Directory工具類。
解壓縮zip包到目標文件夾
1????public?static?void?unConpressZip(String?zipPath,?String?destPath)?throws?IOException?{2????????if(!destPath.endsWith(File.separator)){3????????????destPath?=?destPath?+?File.separator;4????????????File?file?=?new?File(destPath);5????????????if(!file.exists()){6????????????????file.mkdirs();7????????????}8????????}9????????//?新建文件輸入流類, 10????????FileInputStream?fis?=?new?FileInputStream(zipPath); 11 12????????//?給輸入流增加檢驗功能 13????????CheckedInputStream?checkedIns?=?new?CheckedInputStream(fis,new?Adler32()); 14 15????????//?新建zip輸出流,因為讀取的zip格式的文件嘛 16????????ZipInputStream?zipIn?=?new?ZipInputStream(checkedIns); 17 18????????//?增加緩沖流功能,提高性能 19????????BufferedInputStream?buffIn?=?new?BufferedInputStream(zipIn); 20 21????????//?從zip輸入流中讀入每個ZipEntry對象 22????????ZipEntry?zipEntry; 23????????while?((zipEntry?=?zipIn.getNextEntry())?!=?null){ 24????????????System.out.println("解壓中"?+?zipEntry); 25 26????????????//?將解壓的文件寫入到目標文件夾下 27????????????int?size; 28????????????byte[]?buffer?=?new?byte[1024]; 29????????????FileOutputStream?fos?=?new?FileOutputStream(destPath?+?zipEntry.getName()); 30????????????BufferedOutputStream?bos?=?new?BufferedOutputStream(fos,?buffer.length); 31????????????while?((size?=?buffIn.read(buffer,?0,?buffer.length))?!=?-1)?{ 32????????????????bos.write(buffer,?0,?size); 33????????????} 34????????????bos.flush(); 35????????????bos.close(); 36????????} 37????????buffIn.close(); 38 39????????//?輸出校驗和 40????????System.out.println("校驗和:"?+?checkedIns.getChecksum().getValue()); 41????} 42 43????//?在main函數中直接調用 44????public?static?void?main(String[]?args)?throws?IOException?{ 45????????String?dir?=?"d:"; 46????????String?zipPath?=?"d:/test.zip"; 47//????????File[]?files?=?Directory.getLocalFiles(dir,".*\\.txt"); 48//????????ZipFileUtils.compressFiles(files,?zipPath); 49 50????????ZipFileUtils.unConpressZip(zipPath,"F:/ziptest"); 51????}這里解壓zip包還有一種更加簡便的方法,使用ZipFile對象。該對象的entries()方法直接返回ZipEntry類型的枚舉。看下代碼片段:
1????????ZipFile?zipFile?=?new?ZipFile("test.zip"); 2????????Enumeration?e?=?zipFile.entries(); 3????????while?(e.hasMoreElements()){ 4????????????ZipEntry?zipEntry?=?(ZipEntry)?e.nextElement(); 5????????????System.out.println("file:"?+?zipEntry); 6????????}對象序列化
什么是序列化和反序列化呢?
序列化就是將對象轉成字節序列的過程,反序列化就是將字節序列重組成對象的過程。
在這里插入圖片描述
為什么要有對象序列化機制
程序中的對象,其實是存在有內存中,當我們JVM關閉時,無論如何它都不會繼續存在了。那有沒有一種機制能讓對象具有“持久性”呢?序列化機制提供了一種方法,你可以將對象序列化的字節流輸入到文件保存在磁盤上。
序列化機制的另外一種意義便是我們可以通過網絡傳輸對象了,Java中的?遠程方法調用(RMI),底層就需要序列化機制的保證。
在Java中怎么實現序列化和反序列化
首先要序列化的對象必須實現一個Serializable接口(這是一個標識接口,不包括任何方法)
1public?interface?Serializable?{ 2}其次需要是用兩個對象流類:ObjectInputStream 和ObjectOutputStream。主要使用ObjectInputStream對象的readObject方法讀入對象、ObjectOutputStream的writeObject方法寫入對象到流中
下面我們通過序列化機制將一個簡單的pojo對象寫入到文件,并再次讀入到程序內存。
1public?class?User?implements?Serializable?{2????private?String?name;3????private?int?age;45????public?User(String?name,?int?age)?{6????????this.name?=?name;7????????this.age?=?age;8????}9 10????@Override 11????public?String?toString()?{ 12????????return?"User{"?+ 13????????????????"name='"?+?name?+?'\''?+ 14????????????????",?age='"?+?age?+?'\''?+ 15????????????????'}'; 16????} 17 18????public?static?void?main(String[]?args)?throws?IOException,?ClassNotFoundException?{ 19????????User?user?=?new?User("二營長",18); 20????????ObjectOutputStream?objectOps?=?new?ObjectOutputStream(new?FileOutputStream("f:/user.out")); 21????????objectOps.writeObject(user); 22????????objectOps.close(); 23 24????????//?再從文件中取出對象 25????????ObjectInputStream?objectIns?=?new?ObjectInputStream(new?FileInputStream("f:/user.out")); 26 27????????//?這里要做一次強轉 28????????User?user1?=?(User)?objectIns.readObject(); 29????????System.out.println(user1); 30????????objectIns.close(); 31????} 32} 33程序運行結果:
1User{name='二營長',?age='18'}不想序列化的數據使用transient(瞬時)關鍵字屏蔽
如果我們上面的user對象有一個password字段,屬于敏感信息,這種是不能走序列化的方式的,但是實現了Serializable 接口的對象會自動序列化所有的數據域,怎么辦呢?在password字段上加上關鍵字transient就好了。
1?private?transient?String?password;序列化機制就簡單介紹到這里吧。這是Java原生的序列化,現在市面上有好多序列化協議可以選擇,比如Json、FastJson、Thrift、Hessian 、protobuf等。
I/O流的典型使用方式
IO流種類繁多,可以通過不同的方式組合I/O流類,但平時我們常用的也就幾種組合。下盤通過示例的方式盤點幾種I/O流的典型用法。
緩沖輸入文件
1public?class?BufferedInutFile?{2????public?static?String?readFile(String?fileName)?throws?IOException?{3????????BufferedReader?bf?=?new?BufferedReader(new?FileReader(fileName));4????????String?s;56????????//?這里讀取的內容存在了StringBuilder,當然也可以做其他處理7????????StringBuilder?sb?=?new?StringBuilder();8????????while?((s?=?bf.readLine())?!=?null){9????????????sb.append(s?+?"\n"); 10????????} 11????????bf.close(); 12????????return?sb.toString(); 13????} 14 15????public?static?void?main(String[]?args)?throws?IOException?{ 16????????System.out.println(BufferedInutFile.readFile("d:/1.txt")); 17????} 18}格式化內存輸入
要讀取格式化的數據,可以使用DataInputStream。
1public?class?FormattedMemoryInput?{2????public?static?void?main(String[]?args)?throws?IOException?{3????????try?{4????????????DataInputStream?dataIns?=?new?DataInputStream(5????????????????????new?ByteArrayInputStream(BufferedInutFile.readFile("f:/FormattedMemoryInput.java").getBytes()));6????????????while?(true){7????????????????System.out.print((char)?dataIns.readByte());8????????????}9????????}?catch?(EOFException?e)?{ 10????????????System.err.println("End?of?stream"); 11????????} 12????} 13}上面程序會在控制臺輸出當前類本身的所有代碼,并且會拋出一個EOFException異常。拋出異常的原因是已經到留的結尾了還在讀數據。這里可以使用available()做判斷還有多少可以的字符。
1package?com.herp.pattern.strategy;23import?java.io.ByteArrayInputStream;4import?java.io.DataInputStream;5import?java.io.IOException;67public?class?FormattedMemoryInput?{8????public?static?void?main(String[]?args)?throws?IOException?{9????????DataInputStream?dataIns?=?new?DataInputStream( 10????????????????new?ByteArrayInputStream(BufferedInutFile.readFile("FormattedMemoryInput.java").getBytes())); 11????????while?(true){ 12????????????System.out.println((char)?dataIns.readByte()); 13????????} 14????} 15}基本的文件輸出
FileWriter對象可以向文件寫入數據。首先創建一個FileWriter和指定的文件關聯,然后使用BufferedWriter將其包裝提供緩沖功能,為了提供格式化機制,它又被裝飾成為PrintWriter。
1public?class?BasicFileOutput?{2????static?String?file?=?"BasicFileOutput.out";34????public?static?void?main(String[]?args)?throws?IOException?{5????????BufferedReader?in?=?new?BufferedReader(new?StringReader(BufferedInutFile.readFile("f:/BasicFileOutput.java")));6????????PrintWriter?out?=?new?PrintWriter(new?BufferedWriter(new?FileWriter(file)));78????????int?lineCount?=?1;9????????String?s; 10????????while?((s?=?in.readLine())?!=?null){ 11????????????out.println(lineCount?++?+?":?"?+?s); 12????????} 13????????out.close(); 14????????in.close(); 15????} 16}下面是我們寫出的BasicFileOutput.out文件,可以看到我們通過代碼字節加上了行號
11:?package?com.herp.pattern.strategy;22:?33:?import?java.io.*;44:?55:?public?class?BasicFileOutput?{66:?????static?String?file?=?"BasicFileOutput.out";77:?88:?????public?static?void?main(String[]?args)?throws?IOException?{99:?????????BufferedReader?in?=?new?BufferedReader(new?StringReader(BufferedInutFile.readFile("f:/BasicFileOutput"))); 1010:?????????PrintWriter?out?=?new?PrintWriter(new?BufferedWriter(new?FileWriter(file))); 1111:? 1212:?????????int?lineCount?=?1; 1313:?????????String?s; 1414:?????????while?((s?=?in.readLine())?!=?null){ 1515:?????????????out.println(lineCount?++?+?":?"?+?s); 1616:?????????} 1717:?????????out.close(); 1818:?????????in.close(); 1919:?????} 2020:?}數據的存儲和恢復
為了輸出可供另一個“流”恢復的數據,我們需要使用DataOutputStream寫入數據,然后使用DataInputStream恢復數據。當然這些流可以是任何形式(這里的形式其實就是我們前面說過的流的兩端的類型),比如文件。
1public?class?StoringAndRecoveringData?{2????public?static?void?main(String[]?args)?throws?IOException?{3????????DataOutputStream?out?=?new?DataOutputStream(new?BufferedOutputStream(new?FileOutputStream("data.txt")));4????????out.writeDouble(3.1415926);5????????out.writeUTF("三連走起");6????????out.writeInt(125);7????????out.writeUTF("點贊加關注");8????????out.close();9 10????????DataInputStream?in?=?new?DataInputStream(new?BufferedInputStream(new?FileInputStream("data.txt"))); 11????????System.out.println(in.readDouble()); 12????????System.out.println(in.readUTF()); 13????????System.out.println(in.readInt()); 14????????System.out.println(in.readUTF()); 15????????in.close(); 16????} 17}輸出結果:
13.1415926 2三連走起 3125 4點贊加關注需要注意的是我們使用writeUTF()和readUTF()來寫入和讀取字符串。
總結
以上是生活随笔為你收集整理的javaIO学习下:javase学习(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: get 到的html代码如何转码,爬虫网
- 下一篇: 風水渙