java学习笔记16--I/O流和文件
本文地址:http://www.cnblogs.com/archimedes/p/java-study-note16.html,轉(zhuǎn)載請注明源地址。
IO(Input? Output)流
IO流用來處理設備之間的數(shù)據(jù)傳輸,對數(shù)據(jù)的操作是通過流的方式,Java用于操作流的對象都在IO包中
輸入/輸出流可以從以下幾個方面進行分類
從流的方向劃分:
輸入流、輸出流
從流的分工劃分:
節(jié)點流、處理流
從流的內(nèi)容劃分:
面向字符的流、面向字節(jié)的流
字符流和字節(jié)流
字符流的由來: 因為數(shù)據(jù)編碼的不同,而有了對字符進行高效操作的流對象。本質(zhì)其實就是基于字節(jié)流讀取時,去查了指定的碼表。 字節(jié)流和字符流的區(qū)別:
-
讀寫單位不同:字節(jié)流以字節(jié)(8bit)為單位,字符流以字符為單位,根據(jù)碼表映射字符,一次可能讀多個字節(jié)。
-
處理對象不同:字節(jié)流能處理所有類型的數(shù)據(jù)(如圖片、avi等),而字符流只能處理字符類型的數(shù)據(jù)。
結(jié)論:只要是處理純文本數(shù)據(jù),就優(yōu)先考慮使用字符流。 除此之外都使用字節(jié)流。
流按流向分為:輸入流、輸出流
IO流常用基類
字節(jié)流的抽象基類:
?InputStream,OutputStream。
字符流的抽象基類:
?Reader, Writer。
注:由這四個類派生出來的子類名稱都是以其父類名作為子類名的后綴。
如:InputStream的子類FileInputStream。
如:Reader的子類FileReader。
Java流操作有關的類或接口:
Java流類圖結(jié)構:
讀寫文本文件
?寫文本文件
例:在C盤根目錄創(chuàng)建文本文件Hello.txt,并往里寫入若干行文本 import java.io.*; class Ex1{public static void main ( String[] args ) throws IOException { //main方法中聲明拋出IO異常String fileName = "C:\\Hello.txt"; FileWriter writer = new FileWriter( fileName ); writer.write( "Hello!\n"); writer.write( "This is my first text file,\n" ); writer.write( "You can see how this is done.\n" ); writer.write("輸入一行中文也可以\n");writer.close(); } }說明:
每次運行這個程序,都將刪除已經(jīng)存在的”Hello.txt”文件,創(chuàng)建一個新的同名文件。FileWriter的構造方法有五個,本例是通過一個字符串指定文件名來創(chuàng)建。FileWriter類的write方法向文件中寫入字符
Writer類的流可實現(xiàn)內(nèi)部格式到外部磁盤文件格式的轉(zhuǎn)換
“Hello.txt”是一個普通的ASCII碼文本文件,每個英文字符占一個字節(jié),中文字符占兩個字節(jié)
Java程序中的字符串則是每個字符占兩個字節(jié)的,采用Unicode編碼
close方法清空流里的內(nèi)容并關閉它。如果不調(diào)用該方法,可能系統(tǒng)還沒有完成所有數(shù)據(jù)的寫操作,程序就結(jié)束了
在看一個例子:處理IO異常
import java.io.*; class Ex2 {public static void main ( String[] args ) {String fileName = "c:\\Hello.txt" ;try { //將所有IO操作放入try塊中FileWriter writer = new FileWriter( fileName ,true );writer.write( "Hello!\n"); writer.write( "This is my first text file,\n" ); writer.write( "You can see how this is done. \n" );writer.write("輸入一行中文也可以\n");writer.close();}catch ( IOException iox) { System.out.println("Problem writing" + fileName ); }} }說明:
運行此程序,會發(fā)現(xiàn)在原文件內(nèi)容后面又追加了重復的內(nèi)容,這就是將構造方法的第二個參數(shù)設為true的效果
如果將文件屬性改為只讀屬性,再運行本程序,就會出現(xiàn)IO錯誤,程序?qū)⑥D(zhuǎn)入catch塊中,給出出錯信息
BufferedWriter類如果需要寫入的內(nèi)容很多,就應該使用更為高效的緩沖器流類BufferedWriter
FileWriter和BufferedWriter類都用于輸出字符流,包含的方法幾乎完全一樣,但BufferedWriter多提供了一個newLine()方法用于換行
使用BufferedWriter完成上面的功能:
import java.io.*; class Ex3 {public static void main ( String[] args ) throws IOException {String fileName = "C:/newHello.txt" ;BufferedWriter out = new BufferedWriter( new FileWriter( fileName ) );out.write( "Hello!" );out.newLine() ; out.write( "This is another text file using BufferedWriter," ); out.newLine(); ;out.write( "So I can use a common way to start a newline" ); out.close();} }讀文本文件
FileReader類
從文本文件中讀取字符
繼承自Reader抽象類的子類InputStreamReader
BufferedReader
讀文本文件的緩沖器類
具有readLine()方法,可以對換行符進行鑒別,一行一行地讀取輸入流中的內(nèi)容
繼承自Reader
文件輸入方法:
BufferedReader in = new BufferedReader(new FileReader( fileName) );從Hello.txt中讀取文本并顯示在屏幕上
import java.io.*; class Ex4 {public static void main ( String[] args ) {String fileName = "C:/Hello.txt" , line;try {BufferedReader in = new BufferedReader(new FileReader( fileName ) );line = in.readLine(); //讀取一行內(nèi)容while ( line != null ) { System.out.println( line );line = in.readLine();}in.close(); }catch ( IOException iox ) { System.out.println("Problem reading " + fileName ); }} }運行該程序,屏幕上將逐行顯示出Hello.txt文件中的內(nèi)容
FileReader對象:創(chuàng)建后將打開文件,如果文件不存在,會拋出一個IOException
BufferedReader類的readLine()方法:從一個面向字符的輸入流中讀取一行文本。如果其中不再有數(shù)據(jù),返回null
Reader類的read()方法:也可用來判別文件結(jié)束。該方法返回的一個表示某個字符的int型整數(shù),如果讀到文件末尾,返回 -1。據(jù)此,可修改本例中的讀文件部分:
int c; while((c=in.read())!= -1) System.out.print((char)c);close()方法:為了操作系統(tǒng)可以更為有效地利用有限的資源,應該在讀取完畢后,調(diào)用該方法
指定源文件和目標文件名,將源文件的內(nèi)容拷貝至目標文件。調(diào)用方式為:
java copy sourceFile? destinationFile
class CopyMaker {String sourceName, destName;BufferedReader source;BufferedWriter dest;String line;private boolean openFiles() {try {source = new BufferedReader(new FileReader(sourceName));} catch (IOException ex) {System.out.println("Problem opening " + sourceName);return false;}try {dest = new BufferedWriter(new FileWriter(destName));} catch (IOException ex) {System.out.println("Problem opening " + destName);return false;}return true;}private boolean copyFiles() {try {line = source.readLine();while(line != null) {dest.write(line);dest.newLine();line = source.readLine();}} catch (IOException ex) {System.out.println("Problem reading or writing");return false;}return true;}private boolean closeFiles() {boolean retVal = true;try {source.close();} catch (IOException ex) {System.out.println("Prolem closing " + sourceName);retVal = false;}try {dest.close();} catch (IOException ex) {System.out.println("Problem closing " + destName);retVal = false;}return retVal;}public boolean copy(String src, String dst) {sourceName= src;destName = dst;return openFiles() && copyFiles() && closeFiles();} }public class CopyFile {public static void main(String[] args) {if(args.length == 2)new CopyMaker().copy(args[0], args[1]);elseSystem.out.println("Please Enter names");} }讀寫二進制文件
二進制文件
原則上講,所有文件都是由8位的字節(jié)組成的
如果文件字節(jié)中的內(nèi)容應被解釋為字符,則文件被稱為文本文件;如果被解釋為其它含義,則文件被稱為二進制文件
例如文字處理程序,例如字處理軟件Word產(chǎn)生的doc文件中,數(shù)據(jù)要被解釋為字體、格式、圖形和其他非字符信息。因此,這樣的文件是二進制文件,不能用Reader流正確讀取
為什么需要二進制文件?
輸入輸出更快
比文本文件小很多
有些數(shù)據(jù)不容易被表示為字符
抽象類OutputStream
派生類FileOutputStream
用于一般目的輸出(非字符輸出)
用于成組字節(jié)輸出
派生類DataOutputStream
具有寫各種基本數(shù)據(jù)類型的方法
將數(shù)據(jù)寫到另一個輸出流
它在所有的計算機平臺上使用同樣的數(shù)據(jù)格式
其常用的一些方法見下表
例:將三個int型數(shù)字255/0/-1寫入數(shù)據(jù)文件data1.dat
public class ext6_7 {public static void main(String[] args) {String fileName = "c:/data1.dat";int value0 = 255, value1 = 0, value2 = -1;try {DataOutputStream out = new DataOutputStream(new FileOutputStream(fileName));out.writeInt(value0);out.writeInt(value1);out.writeInt(value2);out.close();} catch (IOException ex) {System.out.println("Problem writing " + fileName);}} }說明:
FileOutputStream類的構造方法負責打開文件“data1.dat”用于寫數(shù)據(jù)
FileOutputStream類的對象與DataOutputStream對象連接,寫基本類型的數(shù)據(jù)
BufferedOutputStream
寫二進制文件的緩沖流類
類似于文本文件中的BufferedWriter
對于大量數(shù)據(jù)的寫入,可提高效率
用法示例:
DataOutputStream out = new DataOutputStream(new BufferedOutputStream(new FileOutputStream( fileName ) ) );例:向文件中寫入各種數(shù)據(jù)類型的數(shù),并統(tǒng)計寫入的字節(jié)數(shù)
public class ex6_8 {public static void main(String[] args) throws IOException {String fileName = "c:/mixedTypes.dat";DataOutputStream dataOut = new DataOutputStream(new BufferedOutputStream(new FileOutputStream(fileName)));dataOut.writeInt(0);System.out.println(dataOut.size() + "bytes have been written.");dataOut.writeDouble(31.2);System.out.println(dataOut.size() + "bytes have been written.");dataOut.writeBytes("java");System.out.println(dataOut.size() + "bytes have been written.");dataOut.close();} }讀二進制文件
過濾流
讀或?qū)懙耐瑫r對數(shù)據(jù)進行處理
通過另外一個流來構造一個過濾流
大部分java.io 包所提供過濾流都是FilterInputStream和FilterOutputStream的子類:
DataInputStream 和 DataOutputStream
BufferedInputStream 和 BufferedOutputStream
LineNumberInputStream
PushbackInputStream
PrintStream
讀取上面的例子創(chuàng)建的數(shù)據(jù)文件中的3個int型數(shù)字,顯示相加結(jié)果
public class ex6_10 {public static void main(String[] args) {String fileName = "C:\\data1.dat";int sum = 0;try {DataInputStream instr = new DataInputStream(new BufferedInputStream(new FileInputStream(fileName)));sum += instr.readInt();sum += instr.readInt();sum += instr.readInt();System.out.println("The sum is: " + sum);instr.close();} catch (IOException ex) {System.out.println("Problem reading " + fileName);}} }分析:
readInt方法可以從輸入流中讀入4個字節(jié)并將其當作int型數(shù)據(jù)
由于知道文件中存儲的是3個int型數(shù)據(jù),所以使用了3個讀入語句
如果不知道數(shù)據(jù)的個數(shù)該怎么辦呢?因為DataInputStream的讀入操作如遇到文件結(jié)尾就會拋出EOFException異常,所以我們可以將讀操作放入try塊中
將讀操作放入try塊中,使遇到文件結(jié)尾就會拋出EOFException異常,進入到相應的catch塊中
try {while(true) sum += instr.readInt(); } catch ( EOFException eof ) {System.out.println("The sum is: " + sum);instr.close(); }File類
表示磁盤文件信息
定義了一些與平臺無關的方法來操縱文件
–創(chuàng)建、刪除文件
–重命名文件
–判斷文件的讀寫權限及是否存在
–設置和查詢文件的最近修改時間等
構造文件流可以使用File類的對象作為參數(shù)
File類常用方法:
例:在C盤創(chuàng)建文件Hello.txt,如果存在則刪除舊文件,不存在則直接創(chuàng)建新的
public class ex6_13 {public static void main(String[] args) {File f = new File("C:" + File.separator + "hello.txt");if(f.exists()) {f.delete();} else {try {f.createNewFile();} catch (Exception e) {System.out.println(e.getMessage());}}} }處理壓縮文件
壓縮流類
–java.util.zip包中提供了一些類,使我們可以以壓縮格式對流進行讀寫
–它們都繼承自字節(jié)流類OutputStream和InputStream
–其中GZIPOutputStream和ZipOutputStream可分別把數(shù)據(jù)壓縮成GZIP格式和Zip格式
–GZIPInputStream和ZipInputStream可以分別把壓縮成GZIP格式或Zip的數(shù)據(jù)解壓縮恢復原狀
public class ex6_14 {public static void main(String[] args) throws IOException{FileInputStream in = new FileInputStream("c:/Hello.txt");GZIPOutputStream out = new GZIPOutputStream( //生成壓縮文件test.gznew FileOutputStream("c:/test.gz"));System.out.println("Writing compressing file from" +"c:/Hello.txt to c:/test.gz");int c;while((c = in.read()) != -1) {out.write(c);}in.close();out.close();System.out.println("Reading file form c:/test.gz to monitor");BufferedReader in2 = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream("c:/test.gz"))));String s;while((s = in2.readLine()) != null) System.out.println(s);in2.close();System.out.println("Writing decompression to c:/newHello.txt");GZIPInputStream in3 = new GZIPInputStream( //讀取test.gz中的內(nèi)容new FileInputStream("c:/test.gz"));FileOutputStream out2 = new FileOutputStream("c:/newHello.txt");while((c = in3.read()) != -1) out2.write(c);in3.close();out2.close();} }Zip文件
–可能含有多個文件,所以有多個入口(Entry)
–每個入口用一個ZipEntity對象表示,該對象的getName()方法返回文件的最初名稱
ZipOutputStream
–父類是DeflaterOutputStream
–可以把數(shù)據(jù)壓縮成ZIP格式
ZipInputStream
–父類是InflaterInputStream
–可以把壓縮成ZIP格式的數(shù)據(jù)解壓縮
例:指定若干文件名,將所有文件壓縮為"c:/test.zip",再從此壓縮文件中解壓縮并顯示
public class ex6_15 {public static void main(String[] args) throws IOException {ZipOutputStream out = new ZipOutputStream(new BufferedOutputStream(new FileOutputStream("c:/test.zip")));String[] s = {"c:/t1.txt", "c:/t2.txt", "c:/t3.txt"}; //文件路徑for(int i = 0; i < s.length; i++) {System.out.println("Writing file" + s[i]);BufferedInputStream in = new BufferedInputStream(new FileInputStream(s[i]));out.putNextEntry(new ZipEntry(s[i]));int c;while((c = in.read()) != -1) out.write(c);in.close();}out.close();System.out.println("Reading file");ZipInputStream in2 = new ZipInputStream(new BufferedInputStream(new FileInputStream("c:/test.zip")));ZipEntry ze;while((ze = in2.getNextEntry()) != null) {System.out.println("Reading file " + ze.getName());int x;while((x = in2.read()) != -1) System.out.write(x);System.out.println();}in2.close();} }再看一個例子:解壓縮Zip文件,并恢復其原來路徑
class Unzip {byte[] doc = null; ; //存儲解壓縮數(shù)據(jù)的緩沖字節(jié)數(shù)組String FileName = null; //壓縮文件名字符串String UnZipPath = null; //解壓縮路徑字符串 public Unzip(String filename, String unZipPath) {this.FileName = filename;this.UnZipPath = unZipPath;this.setUnZipPath(this.UnZipPath);}public Unzip(String filename) {this.FileName = new String(filename);this.UnZipPath = null;this.setUnZipPath(this.UnZipPath);}private void setUnZipPath(String unZipPath) {if(unZipPath.endsWith("\\"))this.UnZipPath = new String(unZipPath);elsethis.UnZipPath = new String(unZipPath + "\\");}public void doUnZip() {try {ZipInputStream zipis = new ZipInputStream(new FileInputStream(FileName));ZipEntry fEntry = null;while((fEntry = zipis.getNextEntry()) != null) {if(fEntry.isDirectory()) {checkFilePath(UnZipPath + fEntry.getName());} else { //是文件則解壓縮文件String fname = new String(UnZipPath + fEntry.getName());try {FileOutputStream out = new FileOutputStream(fname);doc = new byte[512];int n;while((n = zipis.read(doc, 0, 512)) != -1) {out.write(doc, 0, n);}out.close();out = null;doc = null;} catch (Exception ex) {} }}zipis.close(); //關閉輸入流} catch (IOException ioe) {System.out.println(ioe);}}private void checkFilePath(String dirName) {File dir = new File(dirName);if(!dir.exists())dir.mkdirs();} } public class ex6_16 {public static void main(String[] args) {String zipFile = "c:/test.zip";String unZipPath = "";Unzip myZip = new Unzip(zipFile, unZipPath);myZip.doUnZip();} }對象序列化
保存對象的信息,在需要的時候,再讀取這個對象
內(nèi)存中的對象在程序結(jié)束時就會被垃圾回收機制清除
用于對象信息存儲和讀取的輸入輸出流類:
ObjectInputStream、ObjectOutputStream
實現(xiàn)對象的讀寫
通過ObjectOutputStream把對象寫入磁盤文件
通過ObjectInputStream把對象讀入程序
–不保存對象的transient和static類型的變量
–對象要想實現(xiàn)序列化,其所屬的類必須實現(xiàn)Serializable接口
必須通過另一個流構造ObjectOutputStream:
FileOutputStream out = new FileOutputStream("theTime"); ObjectOutputStream s = new ObjectOutputStream(out); s.writeObject("Today"); s.writeObject(new Date()); s.flush();必須通過另一個流構造ObjectInputStream:
FileInputStream in = new FileInputStream("theTime"); ObjectInputStream s = new ObjectInputStream(in); String today = (String)s.readObject(); Date date = (Date)s.readObject();空接口,使類的對象可實現(xiàn)序列化
Serializable 接口的定義:
package java.io; public interface Serializable {// there's nothing in here! };實現(xiàn)Serializable接口的語句
public class MyClass implements Serializable {... }使用關鍵字transient可以阻止對象的某些成員被自動寫入文件
看一個例子:
創(chuàng)建一個書籍對象,并把它輸出到一個文件book.dat中,然后再把該對象讀出來,在屏幕上顯示對象信息
class Book implements Serializable {int id;String name;String author;float price;public Book(int id, String name, String author, float price) {this.id = id;this.name = name;this.author = author;this.price = price;} } public class ex6_17 {public static void main(String[] args) throws IOException, ClassNotFoundException {Book book = new Book(100000, "java programming", "Wu", 23);ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("c:/book.dat"));oos.writeObject(book);oos.close();System.out.println("ID is: " + book.id);System.out.println("name is: " + book.name);System.out.println("author is: " + book.author);System.out.println("price is: " + book.price);} }Externalizable 接口
–實現(xiàn)該接口可以控制對象的讀寫
–API中的說明為
public interface Externalizable extends Serializable–其中有兩個方法writeExternal()和readExternal(),因此實現(xiàn)該接口的類必須實現(xiàn)這兩個方法
–ObjectOutputStream的writeObject()方法只寫入對象的標識,然后調(diào)用對象所屬類的writeExternal()
–ObjectInputStream的readObject()方法調(diào)用對象所屬類的readExternal()
隨機文件讀寫
RandomAccessFile類
–可跳轉(zhuǎn)到文件的任意位置讀/寫數(shù)據(jù)
–可在隨機文件中插入數(shù)據(jù),而不破壞該文件的其他數(shù)據(jù)
–實現(xiàn)了DataInput 和 DataOutput 接口,可使用普通的讀寫方法
–有個位置指示器,指向當前讀寫處的位置。剛打開文件時,文件指示器指向文件的開頭處。對文件指針顯式操作的方法有:
int skipBytes(int n):把文件指針向前移動指定的n個字節(jié)
void seek(long):移動文件指針到指定的位置。
long getFilePointer():得到當前的文件指針。
–在等長記錄格式文件的隨機讀取時有很大的優(yōu)勢,但僅限于操作文件,不能訪問其它IO設備,如網(wǎng)絡、內(nèi)存映像等
–可用來實現(xiàn)讀和寫,構造方法包括 public RandomAccessFile(File file,String mode) throws FileNotFoundException public RandomAccessFile(String name, String mode) throws FileNotFoundException –建立一個RandomAccessFile時,要指出你要執(zhí)行的操作:僅從文件讀,還是同時讀寫 new RandomAccessFile("farrago.txt", "r"); new RandomAccessFile("farrago.txt", "rw");RandomAccessFile類常用API
例:創(chuàng)建一個雇員類,包括姓名、年齡。姓名不超過8個字符,年齡是int類型。每條記錄固定為20個字節(jié)。使用RandomAccessFile向文件添加、修改、讀取雇員信息
class Employee {char name[] = {'\u0000', '\u0000','\u0000', '\u0000','\u0000', '\u0000', '\u0000', '\u0000'};int age;public Employee(String name, int age) throws Exception {if(name.toCharArray().length > 8)System.arraycopy(name.toCharArray(), 0, this.name, 0, 8);elseSystem.arraycopy(name.toCharArray(), 0, this.name, 0, name.toCharArray().length);this.age = age;} }public class ex6_18 {String FileName;public ex6_18(String FileName) {this.FileName = FileName;}public void writeEmployee(Employee e, int n) throws Exception {RandomAccessFile ra = new RandomAccessFile(FileName, "rw");ra.seek(n * 20); //將位置指示器移到指定位置上for(int i = 0; i < 8; i++) ra.writeChar(e.name[i]);ra.writeInt(e.age);ra.close();}public void readEmployee(int n) throws Exception {char buf[] = new char[8];RandomAccessFile ra = new RandomAccessFile(FileName, "r");ra.seek(n * 20);for(int i = 0; i < 8; i++) buf[i] = ra.readChar();System.out.print("name: ");System.out.println(buf);System.out.println("age: " + ra.readInt());ra.close();}public static void main(String[] args) throws Exception {ex6_18 t = new ex6_18("c:/temp.txt");Employee e1 = new Employee("zhangsan", 22);Employee e2 = new Employee("lisi", 20);Employee e3 = new Employee("wangwu", 25);t.writeEmployee(e1, 0);t.writeEmployee(e3, 2);System.out.println("第1個雇員的信息");t.readEmployee(0);System.out.println("第3個雇員的信息");t.readEmployee(2);System.out.println("第2個雇員的信息");t.readEmployee(1);} }您還可能感興趣:
java學習筆記系列:
java學習筆記15--多線程編程基礎2
java學習筆記14--多線程編程基礎1?
java學習筆記13--反射機制與動態(tài)代理
java學習筆記12--異常處理
java學習筆記11--集合總結(jié)
java學習筆記10--泛型總結(jié)?
java學習筆記9--內(nèi)部類總結(jié)
java學習筆記8--接口總結(jié)
java學習筆記7--抽象類與抽象方法
java學習筆記6--類的繼承、Object類
java學習筆記5--類的方法?
java學習筆記4--對象的初始化與回收
java學習筆記3--類與對象的基礎?
java學習筆記2--數(shù)據(jù)類型、數(shù)組
java學習筆記1--開發(fā)環(huán)境平臺總結(jié)
《新程序員》:云原生和全面數(shù)字化實踐50位技術專家共同創(chuàng)作,文字、視頻、音頻交互閱讀總結(jié)
以上是生活随笔為你收集整理的java学习笔记16--I/O流和文件的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: java学习笔记15--多线程编程基础2
- 下一篇: java中的深浅克隆