數(shù)據(jù)輸入流:DataInputStream
DataInputStream(InputStream in)
數(shù)據(jù)輸出流:DataOutputStream
DataOutputStream(OutputStream out)
數(shù)據(jù)輸出流允許應(yīng)用程序以適當(dāng)方式將基本 Java 數(shù)據(jù)類型寫入輸出流中。然后,應(yīng)用程序可以使用數(shù)據(jù)輸入流將數(shù)據(jù)讀入。
public class DataStreamDemo { public static void main(String[] args) throws IOException { // 寫 // write(); // 讀 read(); } private static void read() throws IOException { // DataInputStream(InputStream in) // 創(chuàng)建數(shù)據(jù)輸入流對象 DataInputStream dis = new DataInputStream(new FileInputStream("dos.txt")); // 讀數(shù)據(jù) byte b = dis.readByte(); short s = dis.readShort(); int i = dis.readInt(); long l = dis.readLong(); float f = dis.readFloat(); double d = dis.readDouble(); char c = dis.readChar(); boolean bb = dis.readBoolean(); // 釋放資源 dis.close(); System.out.println(b); System.out.println(s); System.out.println(i); System.out.println(l); System.out.println(f); System.out.println(d); System.out.println(c); System.out.println(bb); } private static void write() throws IOException { // DataOutputStream(OutputStream out) // 創(chuàng)建數(shù)據(jù)輸出流對象 DataOutputStream dos = new DataOutputStream(new FileOutputStream("dos.txt")); // 寫數(shù)據(jù) dos.writeByte(10); dos.writeShort(100); dos.writeInt(1000); dos.writeLong(10000); dos.writeFloat(12.34F); dos.writeDouble(12.56); dos.writeChar('a'); dos.writeBoolean(true); // 釋放資源 dos.close(); }}
內(nèi)存操作流一般用于處理臨時信息,因為臨時信息不需要保存,程序結(jié)束,數(shù)據(jù)就從內(nèi)存中消失。
【字節(jié)數(shù)組】
【字符數(shù)組】
【字符串】
public class ByteArrayStreamDemo { public static void main(String[] args) throws IOException { // 寫數(shù)據(jù) // ByteArrayOutputStream() ByteArrayOutputStream baos = new ByteArrayOutputStream(); for (int x = 0; x < 10; x ) { baos.write(("hello" x).getBytes()); } // 釋放資源 // 通過查看源碼我們知道這里什么都沒做,所以根本需要close() // baos.close(); // public byte[] toByteArray() byte[] bys = baos.toByteArray(); // 讀數(shù)據(jù) // ByteArrayInputStream(byte[] buf) ByteArrayInputStream bais = new ByteArrayInputStream(bys); int by = 0; while ((by = bais.read()) != -1) { System.out.print((char) by); } }}
【打印流概述】
【打印流的特點(diǎn)】
pw.println("hello");pw.println(true);pw.println(100);
PrintWriter pw = new PrintWriter(new FileWriter("pw2.txt"), true);這時應(yīng)該調(diào)用println()的方法才可以實現(xiàn)自動刷新,調(diào)用print()不行。而且這個時候不僅僅自動刷新了,還實現(xiàn)了數(shù)據(jù)的換行。println()其實等價于于: bw.write(); bw.newLine(); bw.flush();
流分為基本流和高級流 基本流:就是能夠直接讀寫文件的 高級流:在基本流基礎(chǔ)上提供了一些其他的功能哪些流對象是可以直接操作文本文件的呢?看API,查流對象的構(gòu)造方法,如果同時有File類型和String類型的參數(shù),一般來說就是可以直接操作文件的?! ileInputStream FileOutputStream FileReader FileWriter PrintStream PrintWriter
下面通過一個簡單的PrintWriter案例來進(jìn)行展示:
public class PrintWriterDemo { public static void main(String[] args) throws IOException { // 創(chuàng)建打印流對象 // PrintWriter pw = new PrintWriter("pw.txt"); // 開啟自動刷新 PrintWriter pw = new PrintWriter(new FileWriter("pw.txt"), true); // write只能操作字符串 /*pw.write("hello"); pw.write("world"); pw.write("java");*/ // 要操作任意類型的數(shù)據(jù),要使用print()或println() /*pw.print(true); pw.print(100); pw.print("hello");*/ // 使用print()操作數(shù)據(jù)后,如果沒有調(diào)用close(),數(shù)據(jù)是不會自動刷新的 // 使用println(),并且使用public PrintWriter(Writer out,boolean autoFlush)構(gòu)造方法后,就能使用自動刷新了,即使不調(diào)用close(),數(shù)據(jù)也能寫入到文件 // 此時的println()相當(dāng)于write() newLine() flush()三個方法的綜合 pw.println(true); pw.println(100); pw.println("hello"); // pw.close(); }}
【打印流復(fù)制文本文件】
/** * 需求:PrintWriterDemo.java復(fù)制到Copy.java中 * 數(shù)據(jù)源: * DataStreamDemo.java -- 讀取數(shù)據(jù) -- FileReader -- BufferedReader * 目的地: * Copy.java -- 寫出數(shù)據(jù) -- FileWriter -- BufferedWriter -- PrintWriter */public class CopyFileDemo { public static void main(String[] args) throws IOException { // 以前的版本 /*BufferedReader br = new BufferedReader(new FileReader("PrintWriterDemo.java")); // 封裝目的地 BufferedWriter bw = new BufferedWriter(new FileWriter("Copy.java")); String line = null; while ((line = br.readLine()) != null) { bw.write(line); bw.newLine(); bw.flush(); } bw.close(); br.close();*/ // 打印流的改進(jìn)版 // 封裝數(shù)據(jù)源 BufferedReader br = new BufferedReader(new FileReader("PrintWriterDemo.java")); // 封裝目的地 PrintWriter pw = new PrintWriter(new FileWriter("Copy.java"), true); String line = null; while ((line = br.readLine()) != null) { pw.println(line); } pw.close(); br.close(); }}
? System類中的兩個成員變量:
public static final PrintStream out “標(biāo)準(zhǔn)”輸出流。
可以得知:PrintStream ps = System.out;
public class SystemOutDemo { public static void main(String[] args) { // 有這里的講解我們就知道了,這個輸出語句其本質(zhì)是IO流操作,把數(shù)據(jù)輸出到控制臺。 System.out.println("helloworld"); // 獲取標(biāo)準(zhǔn)輸出流對象 PrintStream ps = System.out; ps.println("helloworld"); ps.println(); // ps.print();//這個方法不存在 System.out.println(); // System.out.print();//這是錯誤的 }}
【轉(zhuǎn)換流的應(yīng)用】
public class SystemOutDemo2 { public static void main(String[] args) throws IOException { // 獲取標(biāo)準(zhǔn)輸出流 // PrintStream ps = System.out; // OutputStream os = ps; // OutputStream os = System.out;//多態(tài) // 我能不能按照剛才使用標(biāo)準(zhǔn)輸入流的方式一樣把數(shù)據(jù)輸出到控制臺呢? // OutputStreamWriter osw = new OutputStreamWriter(os); // BufferedWriter bw = new BufferedWriter(osw); BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out)); bw.write("hello"); bw.newLine(); bw.write("world"); bw.newLine(); bw.write("java"); bw.newLine(); bw.flush(); bw.close(); }}
System.in 標(biāo)準(zhǔn)輸入流。是從鍵盤獲取數(shù)據(jù)的
通過public static final InputStream in,可知InputStream is = System.in
【鍵盤錄入數(shù)據(jù)】
前面我們已經(jīng)學(xué)習(xí)了兩種鍵盤錄入數(shù)據(jù)的方式:
java HelloWorld hello world java
Scanner sc = new Scanner(System.in);String s = sc.nextLine();int x = sc.nextInt()
今天我們再來學(xué)習(xí)一種方式:通過字符緩沖流包裝標(biāo)準(zhǔn)輸入流實現(xiàn)。
獲取標(biāo)準(zhǔn)輸入流的方式是:InputStream is = System.in;
我要怎么實現(xiàn)一次獲取一行數(shù)據(jù)呢?首先你得知道一次讀取一行數(shù)據(jù)的方法是哪個呢?readLine()
而這個方法在哪個類中呢?BufferedReader
所以,你這次應(yīng)該創(chuàng)建BufferedReader的對象,但是底層還是的使用標(biāo)準(zhǔn)輸入流BufferedReader br = new BufferedReader(is);
按照我們的推想,現(xiàn)在應(yīng)該可以了,但是卻報錯了。原因是:字符緩沖流只能針對字符流操作,而你現(xiàn)在是字節(jié)流,所以就會報錯。
那么,我還就想使用了,怎么辦呢?
可以把字節(jié)流轉(zhuǎn)換為字符流,然后在通過字符緩沖流操作
InputStreamReader isr = new InputStreamReader(is);
? ? ? ?BufferedReader br= new BufferedReader(isr);
public class SystemInDemo { public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); System.out.println("請輸入一個字符串:"); String line = br.readLine(); System.out.println("你輸入的字符串是:" line); System.out.println("請輸入一個整數(shù):"); line = br.readLine(); int i = Integer.parseInt(line); System.out.println("你輸入的整數(shù)是:" i); }}
隨機(jī)訪問流:RandomAccessFile類不屬于流,是Object類的子類。但它融合了InputStream和OutputStream的功能。支持對文件的隨機(jī)訪問讀取和寫入。
public RandomAccessFile(String name,String mode):第一個參數(shù)是文件路徑,第二個參數(shù)是操作文件的模式。模式有四種,我們最常用的一種叫"rw",這種方式表示我既可以寫數(shù)據(jù),也可以讀取數(shù)據(jù)
public class RandomAccessFileDemo { public static void main(String[] args) throws IOException { // write(); read(); } private static void read() throws IOException { // 創(chuàng)建隨機(jī)訪問流對象 RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw"); int i = raf.readInt(); System.out.println(i); // 該文件指針可以通過 getFilePointer方法讀取,并通過 seek 方法設(shè)置。 System.out.println("當(dāng)前文件的指針位置是:" raf.getFilePointer()); char ch = raf.readChar(); System.out.println(ch); System.out.println("當(dāng)前文件的指針位置是:" raf.getFilePointer()); String s = raf.readUTF(); System.out.println(s); System.out.println("當(dāng)前文件的指針位置是:" raf.getFilePointer()); // 我不想重頭開始了,我就要讀取a,怎么辦呢? raf.seek(4); ch = raf.readChar(); System.out.println(ch); } private static void write() throws IOException { // 創(chuàng)建隨機(jī)訪問流對象 RandomAccessFile raf = new RandomAccessFile("raf.txt", "rw"); // 怎么玩呢? raf.writeInt(100); raf.writeChar('a'); raf.writeUTF("中國"); raf.close(); }}
SequenceInputStream類可以將多個輸入流串流在一起,合并為一個輸入流,因此,該流也被稱為合并流。
SequenceInputStream(InputStream s1, InputStream s2)
/* * 以前的操作: * a.txt -- b.txt * c.txt -- d.txt * * 現(xiàn)在想要: * a.txt b.txt -- c.txt */public class SequenceInputStreamDemo { public static void main(String[] args) throws IOException { // SequenceInputStream(InputStream s1, InputStream s2) // 需求:把ByteArrayStreamDemo.java和DataStreamDemo.java的內(nèi)容復(fù)制到Copy.java中 InputStream s1 = new FileInputStream("ByteArrayStreamDemo.java"); InputStream s2 = new FileInputStream("DataStreamDemo.java"); SequenceInputStream sis = new SequenceInputStream(s1, s2); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream("Copy.java")); // 如何寫讀寫呢,其實很簡單,你就按照以前怎么讀寫,現(xiàn)在還是怎么讀寫 byte[] bys = new byte[1024]; int len = 0; while ((len = sis.read(bys)) != -1) { bos.write(bys, 0, len); } bos.close(); sis.close(); }}
SequenceInputStream(Enumeration<? extends InputStream> e)
/* * 以前的操作: * a.txt -- b.txt * c.txt -- d.txt * e.txt -- f.txt * * 現(xiàn)在想要: * a.txt b.txt c.txt -- d.txt */public class SequenceInputStreamDemo2 { public static void main(String[] args) throws IOException { // 需求:把下面的三個文件的內(nèi)容復(fù)制到Copy.java中 // ByteArrayStreamDemo.java,CopyFileDemo.java,DataStreamDemo.java // SequenceInputStream(Enumeration e) // 通過簡單的回顧我們知道了Enumeration是Vector中的一個方法的返回值類型。 // Enumeration<E> elements() Vector<InputStream> v = new Vector<InputStream>(); InputStream s1 = new FileInputStream("ByteArrayStreamDemo.java"); InputStream s2 = new FileInputStream("CopyFileDemo.java"); InputStream s3 = new FileInputStream("DataStreamDemo.java"); v.add(s1); v.add(s2); v.add(s3); Enumeration<InputStream> en = v.elements(); SequenceInputStream sis = new SequenceInputStream(en); BufferedOutputStream bos = new BufferedOutputStream( new FileOutputStream("Copy.java")); // 如何寫讀寫呢,其實很簡單,你就按照以前怎么讀寫,現(xiàn)在還是怎么讀寫 byte[] bys = new byte[1024]; int len = 0; while ((len = sis.read(bys)) != -1) { bos.write(bys, 0, len); } bos.close(); sis.close(); }}
由于我們要對對象進(jìn)行序列化,所以我們先自定義一個Person類,該類必須要實現(xiàn)?java.io.Serializable 接口以啟用其序列化功能(該接口居然沒有任何方法,類似于這種沒有方法的接口被稱為標(biāo)記接口)。未實現(xiàn)此接口的類將無法使其任何狀態(tài)序列化或反序列化,并且報NotSerializableException:未序列化異常。
public class Person implements Serializable { private String name; private int age; public Person() { } public Person(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" "name='" name '\'' ", age=" age '}'; }}
編寫測試類
public class ObjectStreamDemo { public static void main(String[] args) throws IOException, ClassNotFoundException { // 序列化數(shù)據(jù)其實就是把對象寫到文本文件 // write(); read(); } private static void read() throws IOException, ClassNotFoundException { // 創(chuàng)建反序列化對象 ObjectInputStream ois = new ObjectInputStream(new FileInputStream("oos.txt")); // 還原對象 Object object = ois.readObject(); // 釋放資源 ois.close(); // 輸出對象 System.out.println(object); } private static void write() throws IOException { // 創(chuàng)建序列化流對象 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("oos.txt")); // 創(chuàng)建對象 Person person = new Person("林青霞", 27); // public final void writeObject(Object obj) oos.writeObject(person); // 釋放資源 oos.close(); }}
依次運(yùn)行write()和read()方法,即可輸出對象內(nèi)容:
這時候,簡單修改Person類的內(nèi)容,比如將age的private修飾符去掉:
public class Person implements Serializable { private String name; int age; ...}?
這時候再測試ObjectStreamDemo,卻發(fā)現(xiàn)程序報錯了:
Exception in thread "main" java.io.InvalidClassException:
day16.Person; local class incompatible: stream classdesc serialVersionUID = -1292889811426758397,
local class serialVersionUID = -26945631235202424
為什么會有問題呢?
Person類實現(xiàn)了序列化接口,那么它本身也應(yīng)該有一個標(biāo)記值。這個標(biāo)記值假設(shè)是100。
開始的時候:
Person.class -- id=100
wirte數(shù)據(jù): oos.txt -- id=100
read數(shù)據(jù): oos.txt -- id=100
在我們修改完P(guān)erson類的內(nèi)容后:
Person.class -- id=200
wirte數(shù)據(jù): oos.txt -- id=100
read數(shù)據(jù): oos.txt -- id=100
此時write/read數(shù)據(jù)的id和Person.class的id不一致,所以就會報以上錯誤。
可是,我們在實際開發(fā)中,可能還需要使用以前寫過的數(shù)據(jù),不能重新寫入。怎么辦呢?
回想一下原因是因為它們的id值不匹配。每次修改java文件的內(nèi)容的時候,class文件的id值都會發(fā)生改變。而讀取文件的時候,會和class文件中的id值進(jìn)行匹配。所以,就會出問題。
但是呢,如果我有辦法,讓這個id值在java文件中是一個固定的值,這樣,你修改文件的時候,這個id值還會發(fā)生改變嗎?
不會?,F(xiàn)在的關(guān)鍵是我如何能夠知道這個id值如何表示的呢?
不用擔(dān)心,你不用記住,也沒關(guān)系,只需自動生成即可。
這樣就能自動生成一個序列化的id值了,而且產(chǎn)生這個值以后,我們對類進(jìn)行任何改動,它讀取以前的數(shù)據(jù)是沒有問題的。
注意:
我一個類中可能有很多的成員變量,有些我不想進(jìn)行序列化。請問該怎么辦呢?
這時候可以使用transient關(guān)鍵字聲明不需要序列化的成員變量。
這時候再去讀取數(shù)據(jù)時,被transient聲明的成員變量就使用的是默認(rèn)值了:
?
來源:http://www.icode9.com/content-1-201901.html